diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2013-03-21 18:58:15 -0400 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2013-03-21 18:58:15 -0400 |
commit | 209f1961ea2d5863d6f2d2e9d2323446ee5e53c4 (patch) | |
tree | d0ba9bc3a057f76755083fb410df16393046ebef /gpu | |
parent | 9bb3596f972cb89ff43554d6d69153f8fde835ca (diff) |
Test tool budget enforcement methods.
Diffstat (limited to 'gpu')
-rw-r--r-- | gpu/budget.cpp | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/gpu/budget.cpp b/gpu/budget.cpp new file mode 100644 index 0000000..f62c515 --- /dev/null +++ b/gpu/budget.cpp | |||
@@ -0,0 +1,235 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <stdint.h> | ||
4 | #include <math.h> | ||
5 | #include <unistd.h> | ||
6 | #include <assert.h> | ||
7 | #include <errno.h> | ||
8 | #include <sys/types.h> | ||
9 | #include <sys/stat.h> | ||
10 | #include <fcntl.h> | ||
11 | |||
12 | /* Include gettid() */ | ||
13 | #include <sys/types.h> | ||
14 | |||
15 | /* Include threading support. */ | ||
16 | #include <pthread.h> | ||
17 | |||
18 | /* Include the LITMUS^RT API.*/ | ||
19 | #include "litmus.h" | ||
20 | |||
21 | #define NUMS 4096 | ||
22 | static int nums[NUMS]; | ||
23 | |||
24 | inline static lt_t cputime_ns(void) | ||
25 | { | ||
26 | struct timespec ts; | ||
27 | lt_t time; | ||
28 | clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); | ||
29 | |||
30 | // safe, as long as sizeof(ls_t) >= 8 | ||
31 | time = s2ns(ts.tv_sec) + ts.tv_nsec; | ||
32 | |||
33 | return time; | ||
34 | } | ||
35 | |||
36 | inline static lt_t wtime_ns(void) | ||
37 | { | ||
38 | struct timespec ts; | ||
39 | lt_t time; | ||
40 | clock_gettime(CLOCK_MONOTONIC, &ts); | ||
41 | |||
42 | // safe, as long as sizeof(ls_t) >= 8 | ||
43 | time = s2ns(ts.tv_sec) + ts.tv_nsec; | ||
44 | |||
45 | return time; | ||
46 | } | ||
47 | |||
48 | static int loop_once(void) | ||
49 | { | ||
50 | int i, j = 0; | ||
51 | for (i = 0; i < NUMS; ++i) | ||
52 | j += nums[i]++; | ||
53 | return j; | ||
54 | } | ||
55 | |||
56 | int loop_for(lt_t time) | ||
57 | { | ||
58 | lt_t end, now; | ||
59 | lt_t last_loop = 0, loop_start; | ||
60 | int dummy = 0; | ||
61 | |||
62 | last_loop = 0; | ||
63 | |||
64 | now = cputime_ns(); | ||
65 | end = now + time; | ||
66 | |||
67 | /* '+ last_loop' attempts to avoid overrun */ | ||
68 | while (now + last_loop < end) { | ||
69 | loop_start = now; | ||
70 | dummy += loop_once(); | ||
71 | now = cputime_ns(); | ||
72 | last_loop = now - loop_start; | ||
73 | } | ||
74 | |||
75 | return dummy; | ||
76 | } | ||
77 | |||
78 | int OVERRUN = 0; | ||
79 | int SIGNALS = 0; | ||
80 | int BLOCK_SIGNALS_ON_SLEEP = 0; | ||
81 | int OVERRUN_RATE = 1; /* default: every job overruns */ | ||
82 | |||
83 | int NUM_JOBS = 0; | ||
84 | int NUM_COMPLETED_JOBS = 0; | ||
85 | int NUM_OVERRUNS = 0; | ||
86 | |||
87 | lt_t overrun_extra = 0; | ||
88 | |||
89 | int job(lt_t exec_ns, lt_t budget_ns) | ||
90 | { | ||
91 | ++NUM_JOBS; | ||
92 | |||
93 | try{ | ||
94 | lt_t approx_remaining = budget_ns; | ||
95 | lt_t now = cputime_ns(); | ||
96 | loop_for(lt_t(exec_ns * 0.9)); /* fudge it a bit to account for overheads */ | ||
97 | |||
98 | if (OVERRUN) { | ||
99 | // do we want to overrun this job? | ||
100 | if ((NUM_JOBS % OVERRUN_RATE) == 0) { | ||
101 | approx_remaining -= (cputime_ns() - now); | ||
102 | |||
103 | if (SIGNALS && BLOCK_SIGNALS_ON_SLEEP) | ||
104 | block_litmus_signals(SIG_BUDGET); | ||
105 | |||
106 | // intentionally overrun via suspension | ||
107 | lt_sleep(approx_remaining + overrun_extra); | ||
108 | |||
109 | if (SIGNALS && BLOCK_SIGNALS_ON_SLEEP) | ||
110 | unblock_litmus_signals(SIG_BUDGET); | ||
111 | } | ||
112 | } | ||
113 | ++NUM_COMPLETED_JOBS; | ||
114 | } | ||
115 | catch (const litmus::sigbudget& e) { | ||
116 | ++NUM_OVERRUNS; | ||
117 | } | ||
118 | |||
119 | sleep_next_period(); | ||
120 | return 1; | ||
121 | } | ||
122 | |||
123 | #define OPTSTR "sboOva" | ||
124 | |||
125 | int main(int argc, char** argv) | ||
126 | { | ||
127 | int ret; | ||
128 | lt_t e_ns = ms2ns(10); | ||
129 | lt_t p_ns = ms2ns(100); | ||
130 | lt_t budget_ns = p_ns/2; | ||
131 | lt_t duration = s2ns(10); | ||
132 | lt_t terminate_time; | ||
133 | unsigned int first_job, last_job; | ||
134 | int opt; | ||
135 | struct rt_task param; | ||
136 | budget_drain_policy_t drain_policy = DRAIN_SIMPLE; | ||
137 | int compute_overrun_rate = 0; | ||
138 | int once = 1; | ||
139 | |||
140 | |||
141 | while ((opt = getopt(argc, argv, OPTSTR)) != -1) { | ||
142 | switch(opt) { | ||
143 | case 's': | ||
144 | SIGNALS = 1; | ||
145 | break; | ||
146 | case 'b': | ||
147 | BLOCK_SIGNALS_ON_SLEEP = 1; | ||
148 | break; | ||
149 | case 'o': | ||
150 | OVERRUN = 1; | ||
151 | overrun_extra = budget_ns/2; | ||
152 | break; | ||
153 | case 'O': | ||
154 | OVERRUN = 1; | ||
155 | overrun_extra = 4*p_ns; | ||
156 | break; | ||
157 | case 'a': | ||
158 | /* select an overrun rate such that a task should be caught | ||
159 | * up from a backlog caused by an overrun before the next | ||
160 | * overrun occurs. | ||
161 | */ | ||
162 | compute_overrun_rate = 1; | ||
163 | break; | ||
164 | case 'v': | ||
165 | drain_policy = DRAIN_SOBLIV; | ||
166 | break; | ||
167 | case ':': | ||
168 | printf("missing argument\n"); | ||
169 | assert(false); | ||
170 | break; | ||
171 | default: | ||
172 | printf("unknown option\n"); | ||
173 | assert(false); | ||
174 | break; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | assert(!BLOCK_SIGNALS_ON_SLEEP || (BLOCK_SIGNALS_ON_SLEEP && SIGNALS)); | ||
179 | |||
180 | if (compute_overrun_rate) { | ||
181 | int backlog = (int)ceil((overrun_extra + budget_ns)/(double)budget_ns); | ||
182 | OVERRUN_RATE = backlog + 2; /* some padding */ | ||
183 | } | ||
184 | |||
185 | init_rt_task_param(¶m); | ||
186 | param.exec_cost = budget_ns; | ||
187 | param.period = p_ns; | ||
188 | param.release_policy = PERIODIC; | ||
189 | param.drain_policy = drain_policy; | ||
190 | if (!SIGNALS) | ||
191 | param.budget_policy = PRECISE_ENFORCEMENT; | ||
192 | else | ||
193 | param.budget_signal_policy = PRECISE_SIGNALS; | ||
194 | |||
195 | init_litmus(); | ||
196 | |||
197 | ret = set_rt_task_param(gettid(), ¶m); | ||
198 | assert(ret == 0); | ||
199 | |||
200 | ret = task_mode(LITMUS_RT_TASK); | ||
201 | assert(ret == 0); | ||
202 | |||
203 | sleep_next_period(); | ||
204 | |||
205 | ret = get_job_no(&first_job); | ||
206 | assert(ret == 0); | ||
207 | |||
208 | terminate_time = duration + wtime_ns(); | ||
209 | |||
210 | while (wtime_ns() < terminate_time) { | ||
211 | try{ | ||
212 | if(once) { | ||
213 | activate_litmus_signals(SIG_BUDGET, litmus::throw_on_litmus_signal); | ||
214 | once = 0; | ||
215 | } | ||
216 | job(e_ns, budget_ns); | ||
217 | } | ||
218 | catch(const litmus::sigbudget &e) { | ||
219 | /* drop silently */ | ||
220 | } | ||
221 | } | ||
222 | |||
223 | ret = get_job_no(&last_job); | ||
224 | assert(ret == 0); | ||
225 | |||
226 | ret = task_mode(BACKGROUND_TASK); | ||
227 | assert(ret == 0); | ||
228 | |||
229 | printf("# Kernel Jobs: %d\n", last_job - first_job + 1); | ||
230 | printf("# User Started Jobs: %d\n", NUM_JOBS); | ||
231 | printf("# User Jobs Completed: %d\n", NUM_COMPLETED_JOBS); | ||
232 | printf("# Overruns: %d\n", NUM_OVERRUNS); | ||
233 | |||
234 | return 0; | ||
235 | } | ||