1
// This file is part of BOINC.
2
// http://boinc.berkeley.edu
3
// Copyright (C) 2008 University of California
5
// BOINC is free software; you can redistribute it and/or modify it
6
// under the terms of the GNU Lesser General Public License
7
// as published by the Free Software Foundation,
8
// either version 3 of the License, or (at your option) any later version.
10
// BOINC is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
// See the GNU Lesser General Public License for more details.
15
// You should have received a copy of the GNU Lesser General Public License
16
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
19
// This file contains functions that can be customized to
20
// implement project-specific scheduling policies.
23
// wu_is_infeasible_custom()
24
// Decide whether host can run a job using a particular app version.
25
// In addition it can:
26
// - set the app version's resource usage and/or FLOPS rate estimate
27
// (by assigning to bav.host_usage)
28
// - modify command-line args
29
// (by assigning to bav.host_usage.cmdline)
30
// - set the job's FLOPS count
31
// (by assigning to wu.rsc_fpops_est)
34
// Decide whether host can use an app version,
35
// and if so what resources it will use
37
// app_plan_uses_gpu():
38
// Which plan classes use GPUs
41
// Determine the value of sending a particular job to host;
42
// (used only by "matchmaker" scheduling)
44
// WARNING: if you modify this file, you must prevent it from
45
// being overwritten the next time you update BOINC source code.
47
// 1) write-protect this file, or
48
// 2) put this in a differently-named file and change the Makefile.am
49
// (and write-protect that)
50
// In either case, put your version under source-code control, e.g. SVN
55
#include "sched_config.h"
56
#include "sched_main.h"
57
#include "sched_msgs.h"
58
#include "sched_send.h"
59
#include "sched_score.h"
60
#include "sched_shmem.h"
61
#include "sched_version.h"
62
#include "sched_customize.h"
64
bool wu_is_infeasible_custom(WORKUNIT& wu, APP& app, BEST_APP_VERSION& bav) {
66
// example: if WU name contains "_v1", don't use CUDA app
67
// Note: this is slightly suboptimal.
68
// If the host is able to accept both GPU and CPU jobs,
69
// we'll skip this job rather than send it for the CPU.
70
// Fixing this would require a big architectural change.
72
if (strstr(wu.name, "_v1") && bav.host_usage.ncudas) {
77
// example: for CUDA app, wu.batch is the minimum number of processors.
78
// Don't send if #procs is less than this.
80
if (!strcmp(app.name, "foobar") && bav.host_usage.ncudas) {
81
int n = g_request->coproc_cuda->prop.multiProcessorCount;
88
// example: if CUDA app and WU name contains ".vlar", don't send
90
if (bav.host_usage.ncudas) {
91
if (strstr(wu.name, ".vlar")) {
99
// Suppose we have a computation that uses two devices alternately.
100
// The devices have speeds s1 and s2.
101
// The fraction of work done on device 1 is frac.
103
// This function returns:
104
// 1) the overall speed
105
// 2) the utilization of device 1, which is always in (0, 1).
107
static inline void coproc_perf(
108
double s1, double s2, double frac,
109
double& speed, double& u1
111
double y = (frac*s2 + (1-frac)*s1);
117
// the following is for an app that can use anywhere from 1 to 64 threads
119
static inline bool app_plan_mt(
120
SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
122
double ncpus = g_wreq->effective_ncpus;
123
// number of usable CPUs, taking user prefs into account
124
int nthreads = (int)ncpus;
125
if (nthreads > 64) nthreads = 64;
126
hu.avg_ncpus = nthreads;
127
hu.max_ncpus = nthreads;
128
sprintf(hu.cmdline, "--nthreads %d", nthreads);
129
hu.projected_flops = sreq.host.p_fpops*hu.avg_ncpus*.99;
130
// the .99 ensures that on uniprocessors a sequential app
131
// will be used in preferences to this
132
hu.peak_flops = sreq.host.p_fpops*hu.avg_ncpus;
133
if (config.debug_version_select) {
134
log_messages.printf(MSG_NORMAL,
135
"[version] %s Multi-thread app projected %.2fGS\n",
136
plan_class, hu.projected_flops/1e9
142
GPU_REQUIREMENTS ati_requirements;
144
static bool ati_check(COPROC_ATI& c, HOST_USAGE& hu,
145
int min_driver_version,
148
double ndevs, // # of GPUs used; can be fractional
149
double cpu_frac, // fraction of FLOPS performed by CPU
152
ati_requirements.update(min_driver_version, min_ram);
155
if (!c.amdrt_detected) {
159
if (!c.atirt_detected) {
163
if (c.version_num < min_driver_version) {
166
if (c.attribs.localRAM*MEGA < min_ram) {
170
hu.gpu_ram = min_ram;
174
g_request->host.p_fpops,
175
hu.natis*c.peak_flops(),
180
hu.peak_flops = hu.natis*c.peak_flops() + hu.avg_ncpus*g_request->host.p_fpops;
181
hu.max_ncpus = hu.avg_ncpus;
182
hu.projected_flops *= flops_scale;
186
#define ATI_MIN_RAM 250*MEGA
187
static inline bool app_plan_ati(
188
SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
190
COPROC_ATI& c = sreq.coprocs.ati;
195
if (!strcmp(plan_class, "ati")) {
196
if (!ati_check(c, hu,
207
if (!strcmp(plan_class, "ati13amd")) {
208
if (!ati_check(c, hu,
219
if (!strcmp(plan_class, "ati13ati")) {
220
if (!ati_check(c, hu,
231
if (!strcmp(plan_class, "ati14")) {
232
if (!ati_check(c, hu,
242
if (!strcmp(plan_class, "ati_opencl")) {
243
// OpenCL not yet supported in standard ATI drivers
247
if (config.debug_version_select) {
248
log_messages.printf(MSG_NORMAL,
249
"[version] %s ATI app projected %.2fG peak %.2fG %.3f CPUs\n",
251
hu.projected_flops/1e9,
259
GPU_REQUIREMENTS cuda_requirements;
261
#define CUDA_MIN_DRIVER_VERSION 17700
262
#define CUDA23_MIN_CUDA_VERSION 2030
263
#define CUDA23_MIN_DRIVER_VERSION 19038
264
#define CUDA3_MIN_CUDA_VERSION 3000
265
#define CUDA3_MIN_DRIVER_VERSION 19500
266
#define CUDA_OPENCL_MIN_DRIVER_VERSION 19713
268
static bool cuda_check(COPROC_CUDA& c, HOST_USAGE& hu,
269
int min_cc, int max_cc,
270
int min_cuda_version, int min_driver_version,
272
double ndevs, // # of GPUs used; can be fractional
273
double cpu_frac, // fraction of FLOPS performed by CPU
276
int cc = c.prop.major*100 + c.prop.minor;
277
if (cc < min_cc) return false;
278
if (max_cc && cc >= max_cc) return false;
280
cuda_requirements.update(min_driver_version, min_ram);
282
// Old BOINC clients report display driver version;
283
// newer ones report CUDA RT version.
284
// Some Linux doesn't return either.
286
if (!c.cuda_version && !c.display_driver_version) {
289
if (c.cuda_version) {
290
if (min_cuda_version && (c.cuda_version < min_cuda_version)) {
294
if (c.display_driver_version) {
295
if (min_driver_version && (c.display_driver_version < min_driver_version)) {
299
if (c.prop.dtotalGlobalMem < min_ram) {
303
hu.gpu_ram = min_ram;
307
g_request->host.p_fpops,
308
hu.ncudas*c.peak_flops(),
313
hu.peak_flops = hu.ncudas*c.peak_flops() + hu.avg_ncpus*g_request->host.p_fpops;
314
hu.max_ncpus = hu.avg_ncpus;
315
hu.projected_flops *= flops_scale;
319
// the following is for an app that uses an NVIDIA GPU
321
static inline bool app_plan_cuda(
322
SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
324
COPROC_CUDA& c = sreq.coprocs.cuda;
329
// Macs require 6.10.28
331
if (strstr(sreq.host.os_name, "Darwin") && (sreq.core_client_version < 61028)) {
335
// for CUDA 2.3, we need to check the CUDA RT version.
336
// Old BOINC clients report display driver version;
337
// newer ones report CUDA RT version
339
if (!strcmp(plan_class, "cuda_fermi")) {
340
if (!cuda_check(c, hu,
342
CUDA3_MIN_CUDA_VERSION, CUDA3_MIN_DRIVER_VERSION,
348
} else if (!strcmp(plan_class, "cuda23")) {
349
if (!cuda_check(c, hu,
351
200, // change to zero if app is compiled to byte code
352
CUDA23_MIN_CUDA_VERSION, CUDA23_MIN_DRIVER_VERSION,
358
} else if (!strcmp(plan_class, "cuda_opencl")) {
359
if (!cuda_check(c, hu,
361
0, CUDA_OPENCL_MIN_DRIVER_VERSION,
367
} else if (!strcmp(plan_class, "cuda")) {
368
if (!cuda_check(c, hu,
370
200, // change to zero if app is compiled to byte code
371
0, CUDA_MIN_DRIVER_VERSION,
378
log_messages.printf(MSG_CRITICAL,
379
"UNKNOWN PLAN CLASS %s\n", plan_class
384
if (config.debug_version_select) {
385
log_messages.printf(MSG_NORMAL,
386
"[version] %s app projected %.2fG peak %.2fG %.3f CPUs\n",
388
hu.projected_flops/1e9,
396
// The following is for a non-CPU-intensive application.
397
// Say that we'll use 1% of a CPU.
398
// This will cause the client (6.7+) to run it at non-idle priority
400
static inline bool app_plan_nci(
401
SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
405
hu.projected_flops = sreq.host.p_fpops*1.01;
406
// The *1.01 is needed to ensure that we'll send this app
407
// version rather than a non-plan-class one
408
hu.peak_flops = sreq.host.p_fpops*.01;
412
// the following is for an app that requires a processor with SSE3,
413
// and will run 10% faster if so
415
static inline bool app_plan_sse3(
416
SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
418
downcase_string(sreq.host.p_features);
419
if (!strstr(sreq.host.p_features, "sse3")) {
420
//add_no_work_message("Your CPU lacks SSE3");
425
hu.projected_flops = 1.1*sreq.host.p_fpops;
426
hu.peak_flops = sreq.host.p_fpops;
430
static inline bool app_plan_opencl_nvidia(
431
SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
436
static inline bool app_plan_opencl_ati(
437
SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
442
// app planning function.
443
// See http://boinc.berkeley.edu/trac/wiki/AppPlan
445
bool app_plan(SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu) {
446
if (!strcmp(plan_class, "mt")) {
447
return app_plan_mt(sreq, plan_class, hu);
448
} else if (strstr(plan_class, "ati")) {
449
return app_plan_ati(sreq, plan_class, hu);
450
} else if (strstr(plan_class, "cuda")) {
451
return app_plan_cuda(sreq, plan_class, hu);
452
} else if (!strcmp(plan_class, "nci")) {
453
return app_plan_nci(sreq, plan_class, hu);
454
} else if (!strcmp(plan_class, "sse3")) {
455
return app_plan_sse3(sreq, plan_class, hu);
457
log_messages.printf(MSG_CRITICAL,
458
"Unknown plan class: %s\n", plan_class
463
// the following is used to enforce limits on in-progress jobs
464
// for GPUs and CPUs (see handle_request.cpp)
466
bool app_plan_uses_gpu(const char* plan_class) {
467
if (strstr(plan_class, "cuda")) {
470
if (strstr(plan_class, "ati")) {
476
// compute a "score" for sending this job to this host.
477
// Return false if the WU is infeasible.
478
// Otherwise set est_time and disk_usage.
480
bool JOB::get_score() {
484
WU_RESULT& wu_result = ssp->wu_results[index];
485
wu = wu_result.workunit;
486
app = ssp->lookup_app(wu.appid);
490
// Find the best app version to use.
492
bavp = get_app_version(wu, true, false);
493
if (!bavp) return false;
495
retval = wu_is_infeasible_fast(
496
wu, wu_result.res_server_state, wu_result.res_priority,
497
wu_result.res_report_deadline,
501
if (config.debug_send) {
502
log_messages.printf(MSG_NORMAL,
503
"[send] [HOST#%d] [WU#%d %s] WU is infeasible: %s\n",
504
g_reply->host.id, wu.id, wu.name, infeasible_string(retval)
513
// example: for CUDA app, wu.batch is the minimum number of processors.
514
// add min/actual to score
515
// (this favors sending jobs that need lots of procs to GPUs that have them)
516
// IF YOU USE THIS, USE THE PART IN wu_is_infeasible_custom() ALSO
518
if (!strcmp(app->name, "foobar") && bavp->host_usage.ncudas) {
519
int n = g_request->coproc_cuda->prop.multiProcessorCount;
520
score += ((double)wu.batch)/n;
524
// check if user has selected apps,
525
// and send beta work to beta users
527
if (app->beta && !config.distinct_beta_apps) {
528
if (g_wreq->allow_beta_work) {
534
if (app_not_selected(wu)) {
535
if (!g_wreq->allow_non_preferred_apps) {
538
// Allow work to be sent, but it will not get a bump in its score
545
// if job needs to get done fast, send to fast/reliable host
547
if (bavp->reliable && (wu_result.need_reliable)) {
551
// if job already committed to an HR class,
552
// try to send to host in that class
554
if (wu_result.infeasible_count) {
558
// Favor jobs that will run fast
560
score += bavp->host_usage.projected_flops/1e9;
562
// match large jobs to fast hosts
564
if (config.job_size_matching) {
565
double host_stdev = (g_reply->host.p_fpops - ssp->perf_info.host_fpops_mean)/ ssp->perf_info.host_fpops_stdev;
566
double diff = host_stdev - wu_result.fpops_size;
570
// TODO: If user has selected some apps but will accept jobs from others,
571
// try to send them jobs from the selected apps
574
est_time = estimate_duration(wu, *bavp);
575
disk_usage = wu.rsc_disk_bound;