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()"
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
10261026static inline bool in_run_list (vector<RESULT *>& run_list, ACTIVE_TASK * atp) {
0 commit comments