Skip to content

Commit 1e0b441

Browse files
authored
Merge pull request #7035 from BOINC/dpa_cpu_sched
client: fix sched bug that could cause idle devices
2 parents e0d7462 + 34b1830 commit 1e0b441

2 files changed

Lines changed: 51 additions & 49 deletions

File tree

client/cpu_sched.cpp

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,12 @@
2323
// - for a given resource, jobs in deadline danger first
2424
// - jobs from projects with lower recent est. credit first
2525
// In principle, the run list could include all runnable jobs.
26-
// For efficiency, we stop adding:
26+
// But for efficiency, we stop adding:
2727
// - GPU jobs: when all GPU instances used
28-
// - CPU jobs: when the # of CPUs allocated to single-thread jobs,
29-
// OR the # allocated to multi-thread jobs, exceeds # CPUs
30-
// (ensure we have enough single-thread jobs
31-
// in case we can't run the multi-thread jobs)
32-
// NOTE: RAM usage is not taken into consideration
33-
// in the process of building this list.
34-
// It's possible that we include a bunch of jobs that can't run
35-
// because of memory limits,
36-
// even though there are other jobs that could run.
28+
// - CPU jobs: when all CPUs used, with a 2X slop factor
29+
// so we have enough jobs even if some can't run
30+
// because of their RAM usage.
31+
// (RAM usage is not taken into consideration in building this list)
3732
// - add running jobs to the list
3833
// (in case they haven't finished time slice or checkpointed)
3934
// - sort the list according to "more_important()"
@@ -43,9 +38,7 @@
4338
// other jobs (enforce_run_list).
4439
// Don't run a job if
4540
// - its GPUs can't be assigned (possible if need >1 GPU)
46-
// - it's a multi-thread job, and CPU usage would be #CPUs+1 or more
47-
// - it's a single-thread job, don't oversaturate CPU
48-
// (details depend on whether a MT job is running)
41+
// - it would use too many CPUs (see details below)
4942
// - its memory usage would exceed RAM limits
5043
// If there's a running job using a given app version,
5144
// unstarted jobs using that app version
@@ -103,28 +96,42 @@ struct PROC_RESOURCES {
10396
pr_coprocs.clear_usage();
10497
}
10598

106-
// should we stop scanning jobs?
99+
// In make_run_list(), should we stop scanning CPU jobs?
100+
// factors:
101+
// - we need to stop sometime; if there are 1000 jobs
102+
// it would be inefficient to scan them all
103+
// - some jobs might use too much RAM to run,
104+
// so we scan jobs up to 2X #CPUs
107105
//
108106
inline bool stop_scan_cpu() {
109-
if (ncpus_used_st >= ncpus) return true;
107+
if (ncpus_used_st >= 2*ncpus) {
108+
return true;
109+
}
110110
if (ncpus_used_mt >= 2*ncpus) return true;
111111
// kind of arbitrary, but need to have some limit
112112
// in case there are only MT jobs, and lots of them
113113
return false;
114114
}
115115

116+
// similar, for GPU
117+
//
116118
inline bool stop_scan_coproc(int rsc_type) {
117119
COPROC& cp = pr_coprocs.coprocs[rsc_type];
118120
for (int i=0; i<cp.count; i++) {
119121
if (cp.usage[i] < 1) return false;
122+
// could make it 2 to provide more jobs
123+
// for RAM exceeded case
120124
}
121125
return true;
122126
}
123127

124-
// Should we add this job to the runnable list?
125-
// There are two possible reasons not to:
128+
// Should we add this job to the run list?
129+
// reasons not to:
126130
// - the job won't be able to run (e.g. it's being aborted)
127-
// - the runnable list has enough jobs of this type already
131+
// - it's backed off
132+
// - it's GPU, and GPUs are suspended
133+
// - it's Docker, and Docker not installed or not inited
134+
// - it's GPU, and run list already commits the needed GPUs
128135
//
129136
bool can_schedule(RESULT* rp, ACTIVE_TASK* atp) {
130137
if (atp) {
@@ -157,20 +164,6 @@ struct PROC_RESOURCES {
157164
if (!sufficient_coprocs(*rp)) {
158165
return false;
159166
}
160-
} else {
161-
// CPU jobs: see if we have enough already in list
162-
//
163-
if (rp->resource_usage.avg_ncpus > 1) {
164-
if (ncpus_used_mt > 0) {
165-
if (ncpus_used_mt + rp->resource_usage.avg_ncpus > ncpus) {
166-
return false;
167-
}
168-
}
169-
} else {
170-
if (ncpus_used_st >= ncpus) {
171-
return false;
172-
}
173-
}
174167
}
175168

176169
// if job uses Docker, make sure it's installed
@@ -252,6 +245,8 @@ struct PROC_RESOURCES {
252245
adjust_rec_sched(rp);
253246
}
254247

248+
// are there enough unused coproc instances to run this job?
249+
//
255250
bool sufficient_coprocs(RESULT& r) {
256251
int rt = r.resource_usage.rsc_type;
257252
if (!rt) return true;
@@ -358,8 +353,7 @@ static inline bool finished_time_slice(ACTIVE_TASK* atp) {
358353
// Values are returned in project->next_runnable_result
359354
// (skip projects for which this is already non-NULL)
360355
//
361-
// Don't choose results with already_selected == true;
362-
// mark chosen results as already_selected.
356+
// Don't choose results with already_selected == true
363357
//
364358
// The preference order:
365359
// 1. results with active tasks that are running
@@ -414,15 +408,6 @@ void CLIENT_STATE::assign_results_to_projects() {
414408
if (project->next_runnable_result) continue;
415409
project->next_runnable_result = rp;
416410
}
417-
418-
// mark selected results, so CPU scheduler won't try to consider
419-
// a result more than once
420-
//
421-
for (PROJECT *project: projects) {
422-
if (project->next_runnable_result) {
423-
project->next_runnable_result->already_selected = true;
424-
}
425-
}
426411
}
427412

428413
// Among projects with a "next runnable result",
@@ -435,14 +420,18 @@ RESULT* CLIENT_STATE::highest_prio_project_best_result() {
435420
bool first = true;
436421

437422
for (PROJECT *p: projects) {
438-
if (!p->next_runnable_result) continue;
423+
if (!p->next_runnable_result) {
424+
continue;
425+
}
439426
if (first || p->sched_priority > best_prio) {
440427
first = false;
441428
best_project = p;
442429
best_prio = p->sched_priority;
443430
}
444431
}
445-
if (!best_project) return NULL;
432+
if (!best_project) {
433+
return NULL;
434+
}
446435

447436
RESULT* rp = best_project->next_runnable_result;
448437
best_project->next_runnable_result = 0;
@@ -892,7 +881,7 @@ void CLIENT_STATE::make_run_list(vector<RESULT*>& run_list) {
892881
PROC_RESOURCES proc_rsc;
893882

894883
if (log_flags.cpu_sched_debug) {
895-
msg_printf(0, MSG_INFO, "[cpu_sched_debug] schedule_cpus(): start");
884+
msg_printf(0, MSG_INFO, "[cpu_sched_debug] make_run_list(): start");
896885
}
897886

898887
proc_rsc.init();
@@ -980,13 +969,17 @@ void CLIENT_STATE::make_run_list(vector<RESULT*>& run_list) {
980969
#endif
981970
while (!proc_rsc.stop_scan_cpu()) {
982971
RESULT *rp = earliest_deadline_result(RSC_TYPE_CPU);
983-
if (!rp) break;
972+
if (!rp) {
973+
break;
974+
}
984975
rp->already_selected = true;
985976
if (have_max_concurrent && max_concurrent_exceeded(rp)) {
986977
continue;
987978
}
988979
ACTIVE_TASK *atp = lookup_active_task_by_result(rp);
989-
if (!proc_rsc.can_schedule(rp, atp)) continue;
980+
if (!proc_rsc.can_schedule(rp, atp)) {
981+
continue;
982+
}
990983
proc_rsc.schedule(rp, atp, true);
991984
rp->project->rsc_pwf[0].deadlines_missed_copy--;
992985
rp->edf_scheduled = true;
@@ -1010,17 +1003,24 @@ void CLIENT_STATE::make_run_list(vector<RESULT*>& run_list) {
10101003
if (!rp) {
10111004
break;
10121005
}
1006+
rp->already_selected = true;
10131007
if (have_max_concurrent && max_concurrent_exceeded(rp)) {
10141008
continue;
10151009
}
10161010
ACTIVE_TASK *atp = lookup_active_task_by_result(rp);
1017-
if (!proc_rsc.can_schedule(rp, atp)) continue;
1011+
if (!proc_rsc.can_schedule(rp, atp)) {
1012+
continue;
1013+
}
10181014
proc_rsc.schedule(rp, atp, false);
10191015
run_list.push_back(rp);
10201016
if (have_max_concurrent) {
10211017
max_concurrent_inc(rp);
10221018
}
10231019
}
1020+
1021+
if (log_flags.cpu_sched_debug) {
1022+
msg_printf(0, MSG_INFO, "[cpu_sched_debug] make_run_list(): end");
1023+
}
10241024
}
10251025

10261026
static inline bool in_run_list(vector<RESULT*>& run_list, ACTIVE_TASK* atp) {

client/sim.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,8 @@ void do_client_simulation() {
14451445
set_no_rsc_config();
14461446
process_gpu_exclusions();
14471447

1448+
gstate.init_result_resource_usage();
1449+
14481450
get_app_params();
14491451
if (!include_empty_projects) {
14501452
cull_projects();

0 commit comments

Comments
 (0)