~ubuntu-branches/ubuntu/precise/boinc/precise

« back to all changes in this revision

Viewing changes to sched/sched_customize.cpp

Tags: 6.12.8+dfsg-1
* New upstream release.
* Simplified debian/rules

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// This file is part of BOINC.
 
2
// http://boinc.berkeley.edu
 
3
// Copyright (C) 2008 University of California
 
4
//
 
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.
 
9
//
 
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.
 
14
//
 
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/>.
 
17
//
 
18
 
 
19
// This file contains functions that can be customized to
 
20
// implement project-specific scheduling policies.
 
21
// The functions are:
 
22
//
 
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)
 
32
//
 
33
// app_plan()
 
34
//      Decide whether host can use an app version,
 
35
//      and if so what resources it will use
 
36
//
 
37
// app_plan_uses_gpu():
 
38
//      Which plan classes use GPUs
 
39
//
 
40
// JOB::get_score():
 
41
//      Determine the value of sending a particular job to host;
 
42
//      (used only by "matchmaker" scheduling)
 
43
//
 
44
// WARNING: if you modify this file, you must prevent it from
 
45
// being overwritten the next time you update BOINC source code.
 
46
// You can either:
 
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
 
51
 
 
52
#include "str_util.h"
 
53
#include "util.h"
 
54
 
 
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"
 
63
 
 
64
bool wu_is_infeasible_custom(WORKUNIT& wu, APP& app, BEST_APP_VERSION& bav) {
 
65
#if 0
 
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.
 
71
    //
 
72
    if (strstr(wu.name, "_v1") && bav.host_usage.ncudas) {
 
73
        return true;
 
74
    }
 
75
#endif
 
76
#if 0
 
77
    // example: for CUDA app, wu.batch is the minimum number of processors.
 
78
    // Don't send if #procs is less than this.
 
79
    //
 
80
    if (!strcmp(app.name, "foobar") && bav.host_usage.ncudas) {
 
81
        int n = g_request->coproc_cuda->prop.multiProcessorCount;
 
82
        if (n < wu.batch) {
 
83
           return true;
 
84
        }
 
85
    }
 
86
#endif
 
87
#if 0
 
88
    // example: if CUDA app and WU name contains ".vlar", don't send
 
89
    //
 
90
    if (bav.host_usage.ncudas) {
 
91
        if (strstr(wu.name, ".vlar")) {
 
92
            return true;
 
93
        }
 
94
    }
 
95
#endif
 
96
    return false;
 
97
}
 
98
 
 
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.
 
102
//
 
103
// This function returns:
 
104
// 1) the overall speed
 
105
// 2) the utilization of device 1, which is always in (0, 1).
 
106
//
 
107
static inline void coproc_perf(
 
108
    double s1, double s2, double frac,
 
109
    double& speed, double& u1
 
110
) {
 
111
    double y = (frac*s2 + (1-frac)*s1);
 
112
    speed = s1*s2/y;
 
113
        // do the math
 
114
    u1 = frac*s2/y;
 
115
}
 
116
 
 
117
// the following is for an app that can use anywhere from 1 to 64 threads
 
118
//
 
119
static inline bool app_plan_mt(
 
120
    SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
 
121
) {
 
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
 
137
        );
 
138
    }
 
139
    return true;
 
140
}
 
141
 
 
142
GPU_REQUIREMENTS ati_requirements;
 
143
 
 
144
static bool ati_check(COPROC_ATI& c, HOST_USAGE& hu,
 
145
    int min_driver_version,
 
146
    bool need_amd_libs,
 
147
    double min_ram,
 
148
    double ndevs,       // # of GPUs used; can be fractional
 
149
    double cpu_frac,    // fraction of FLOPS performed by CPU
 
150
    double flops_scale
 
151
) {
 
152
    ati_requirements.update(min_driver_version, min_ram);
 
153
 
 
154
    if (need_amd_libs) {
 
155
        if (!c.amdrt_detected) {
 
156
            return false;
 
157
        }
 
158
    } else {
 
159
        if (!c.atirt_detected) {
 
160
            return false;
 
161
        }
 
162
    }
 
163
    if (c.version_num < min_driver_version) {
 
164
        return false;
 
165
    }
 
166
    if (c.attribs.localRAM*MEGA < min_ram) {
 
167
        return false;
 
168
    }
 
169
 
 
170
    hu.gpu_ram = min_ram;
 
171
    hu.natis = ndevs;
 
172
 
 
173
    coproc_perf(
 
174
        g_request->host.p_fpops,
 
175
        hu.natis*c.peak_flops(),
 
176
        cpu_frac,
 
177
        hu.projected_flops,
 
178
        hu.avg_ncpus
 
179
    );
 
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;
 
183
    return true;
 
184
}
 
185
 
 
186
#define ATI_MIN_RAM 250*MEGA
 
187
static inline bool app_plan_ati(
 
188
    SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
 
189
) {
 
190
    COPROC_ATI& c = sreq.coprocs.ati;
 
191
    if (!c.count) {
 
192
        return false;
 
193
    }
 
194
 
 
195
    if (!strcmp(plan_class, "ati")) {
 
196
        if (!ati_check(c, hu,
 
197
            1000000,
 
198
            true,
 
199
            ATI_MIN_RAM,
 
200
            1, .01,
 
201
            1
 
202
        )) {
 
203
            return false;
 
204
        }
 
205
    }
 
206
 
 
207
    if (!strcmp(plan_class, "ati13amd")) {
 
208
        if (!ati_check(c, hu,
 
209
            1003000,
 
210
            true,
 
211
            ATI_MIN_RAM,
 
212
            1, .01,
 
213
            1.01
 
214
        )) {
 
215
            return false;
 
216
        }
 
217
    }
 
218
 
 
219
    if (!strcmp(plan_class, "ati13ati")) {
 
220
        if (!ati_check(c, hu,
 
221
            1003186,
 
222
            false,
 
223
            ATI_MIN_RAM,
 
224
            1, .01,
 
225
            1.02
 
226
        )) {
 
227
            return false;
 
228
        }
 
229
    }
 
230
 
 
231
    if (!strcmp(plan_class, "ati14")) {
 
232
        if (!ati_check(c, hu,
 
233
            1004000,
 
234
            false,
 
235
            ATI_MIN_RAM,
 
236
            1, .01,
 
237
            1.03
 
238
        )) {
 
239
            return false;
 
240
        }
 
241
    }
 
242
    if (!strcmp(plan_class, "ati_opencl")) {
 
243
        // OpenCL not yet supported in standard ATI drivers
 
244
        return false;
 
245
    }
 
246
 
 
247
    if (config.debug_version_select) {
 
248
        log_messages.printf(MSG_NORMAL,
 
249
            "[version] %s ATI app projected %.2fG peak %.2fG %.3f CPUs\n",
 
250
            plan_class,
 
251
            hu.projected_flops/1e9,
 
252
            hu.peak_flops/1e9,
 
253
            hu.avg_ncpus
 
254
        );
 
255
    }
 
256
    return true;
 
257
}
 
258
 
 
259
GPU_REQUIREMENTS cuda_requirements;
 
260
 
 
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
 
267
 
 
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,
 
271
    double min_ram,
 
272
    double ndevs,       // # of GPUs used; can be fractional
 
273
    double cpu_frac,    // fraction of FLOPS performed by CPU
 
274
    double flops_scale
 
275
) {
 
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;
 
279
 
 
280
    cuda_requirements.update(min_driver_version, min_ram);
 
281
 
 
282
    // Old BOINC clients report display driver version;
 
283
    // newer ones report CUDA RT version.
 
284
    // Some Linux doesn't return either.
 
285
    //
 
286
    if (!c.cuda_version && !c.display_driver_version) {
 
287
        return false;
 
288
    }
 
289
    if (c.cuda_version) {
 
290
        if (min_cuda_version && (c.cuda_version < min_cuda_version)) {
 
291
            return false;
 
292
        }
 
293
    }
 
294
    if (c.display_driver_version) {
 
295
        if (min_driver_version && (c.display_driver_version < min_driver_version)) {
 
296
            return false;
 
297
        }
 
298
    }
 
299
    if (c.prop.dtotalGlobalMem < min_ram) {
 
300
        return false;
 
301
    }
 
302
 
 
303
    hu.gpu_ram = min_ram;
 
304
    hu.ncudas = ndevs;
 
305
 
 
306
    coproc_perf(
 
307
        g_request->host.p_fpops,
 
308
        hu.ncudas*c.peak_flops(),
 
309
        cpu_frac,
 
310
        hu.projected_flops,
 
311
        hu.avg_ncpus
 
312
    );
 
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;
 
316
    return true;
 
317
}
 
318
 
 
319
// the following is for an app that uses an NVIDIA GPU
 
320
//
 
321
static inline bool app_plan_cuda(
 
322
    SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
 
323
) {
 
324
    COPROC_CUDA& c = sreq.coprocs.cuda;
 
325
    if (!c.count) {
 
326
        return false;
 
327
    }
 
328
 
 
329
    // Macs require 6.10.28
 
330
    //
 
331
    if (strstr(sreq.host.os_name, "Darwin") && (sreq.core_client_version < 61028)) {
 
332
        return false;
 
333
    }
 
334
 
 
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
 
338
    //
 
339
    if (!strcmp(plan_class, "cuda_fermi")) {
 
340
        if (!cuda_check(c, hu,
 
341
            200, 0,
 
342
            CUDA3_MIN_CUDA_VERSION, CUDA3_MIN_DRIVER_VERSION,
 
343
            384*MEGA,
 
344
            1, .01, 1.02
 
345
        )) {
 
346
            return false;
 
347
        }
 
348
    } else if (!strcmp(plan_class, "cuda23")) {
 
349
        if (!cuda_check(c, hu,
 
350
            100,
 
351
            200,    // change to zero if app is compiled to byte code
 
352
            CUDA23_MIN_CUDA_VERSION, CUDA23_MIN_DRIVER_VERSION,
 
353
            384*MEGA,
 
354
            1, .01, 1.01
 
355
        )) {
 
356
            return false;
 
357
        }
 
358
    } else if (!strcmp(plan_class, "cuda_opencl")) {
 
359
        if (!cuda_check(c, hu,
 
360
            100, 0,
 
361
            0, CUDA_OPENCL_MIN_DRIVER_VERSION,
 
362
            384*MEGA,
 
363
            1, .01, 1
 
364
        )) {
 
365
            return false;
 
366
        }
 
367
    } else if (!strcmp(plan_class, "cuda")) {
 
368
        if (!cuda_check(c, hu,
 
369
            100,
 
370
            200,    // change to zero if app is compiled to byte code
 
371
            0, CUDA_MIN_DRIVER_VERSION,
 
372
            254*MEGA,
 
373
            1, .01, 1
 
374
        )) {
 
375
            return false;
 
376
        }
 
377
    } else {
 
378
        log_messages.printf(MSG_CRITICAL,
 
379
            "UNKNOWN PLAN CLASS %s\n", plan_class
 
380
        );
 
381
        return false;
 
382
    }
 
383
 
 
384
    if (config.debug_version_select) {
 
385
        log_messages.printf(MSG_NORMAL,
 
386
            "[version] %s app projected %.2fG peak %.2fG %.3f CPUs\n",
 
387
            plan_class,
 
388
            hu.projected_flops/1e9,
 
389
            hu.peak_flops/1e9,
 
390
            hu.avg_ncpus
 
391
        );
 
392
    }
 
393
    return true;
 
394
}
 
395
 
 
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
 
399
//
 
400
static inline bool app_plan_nci(
 
401
    SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
 
402
) {
 
403
    hu.avg_ncpus = .01;
 
404
    hu.max_ncpus = .01;
 
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;
 
409
    return true;
 
410
}
 
411
 
 
412
// the following is for an app that requires a processor with SSE3,
 
413
// and will run 10% faster if so
 
414
//
 
415
static inline bool app_plan_sse3(
 
416
    SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
 
417
) {
 
418
    downcase_string(sreq.host.p_features);
 
419
    if (!strstr(sreq.host.p_features, "sse3")) {
 
420
        //add_no_work_message("Your CPU lacks SSE3");
 
421
        return false;
 
422
    }
 
423
    hu.avg_ncpus = 1;
 
424
    hu.max_ncpus = 1;
 
425
    hu.projected_flops = 1.1*sreq.host.p_fpops;
 
426
    hu.peak_flops = sreq.host.p_fpops;
 
427
    return true;
 
428
}
 
429
 
 
430
static inline bool app_plan_opencl_nvidia(
 
431
    SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
 
432
) {
 
433
    return false;
 
434
}
 
435
 
 
436
static inline bool app_plan_opencl_ati(
 
437
    SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
 
438
) {
 
439
    return false;
 
440
}
 
441
 
 
442
// app planning function.
 
443
// See http://boinc.berkeley.edu/trac/wiki/AppPlan
 
444
//
 
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);
 
456
    }
 
457
    log_messages.printf(MSG_CRITICAL,
 
458
        "Unknown plan class: %s\n", plan_class
 
459
    );
 
460
    return false;
 
461
}
 
462
 
 
463
// the following is used to enforce limits on in-progress jobs
 
464
// for GPUs and CPUs (see handle_request.cpp)
 
465
//
 
466
bool app_plan_uses_gpu(const char* plan_class) {
 
467
    if (strstr(plan_class, "cuda")) {
 
468
        return true;
 
469
    }
 
470
    if (strstr(plan_class, "ati")) {
 
471
        return true;
 
472
    }
 
473
    return false;
 
474
}
 
475
 
 
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.
 
479
//
 
480
bool JOB::get_score() {
 
481
    WORKUNIT wu;
 
482
    int retval;
 
483
 
 
484
    WU_RESULT& wu_result = ssp->wu_results[index];
 
485
    wu = wu_result.workunit;
 
486
    app = ssp->lookup_app(wu.appid);
 
487
 
 
488
    score = 0;
 
489
 
 
490
    // Find the best app version to use.
 
491
    //
 
492
    bavp = get_app_version(wu, true, false);
 
493
    if (!bavp) return false;
 
494
 
 
495
    retval = wu_is_infeasible_fast(
 
496
        wu, wu_result.res_server_state, wu_result.res_priority,
 
497
        wu_result.res_report_deadline,
 
498
        *app, *bavp
 
499
    );
 
500
    if (retval) {
 
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)
 
505
            );
 
506
        }
 
507
        return false;
 
508
    }
 
509
 
 
510
    score = 1;
 
511
 
 
512
#if 0
 
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
 
517
    //
 
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;
 
521
    }
 
522
#endif
 
523
 
 
524
    // check if user has selected apps,
 
525
    // and send beta work to beta users
 
526
    //
 
527
    if (app->beta && !config.distinct_beta_apps) {
 
528
        if (g_wreq->allow_beta_work) {
 
529
            score += 1;
 
530
        } else {
 
531
            return false;
 
532
        }
 
533
    } else {
 
534
        if (app_not_selected(wu)) {
 
535
            if (!g_wreq->allow_non_preferred_apps) {
 
536
                return false;
 
537
            } else {
 
538
            // Allow work to be sent, but it will not get a bump in its score
 
539
            }
 
540
        } else {
 
541
            score += 1;
 
542
        }
 
543
    }
 
544
            
 
545
    // if job needs to get done fast, send to fast/reliable host
 
546
    //
 
547
    if (bavp->reliable && (wu_result.need_reliable)) {
 
548
        score += 1;
 
549
    }
 
550
    
 
551
    // if job already committed to an HR class,
 
552
    // try to send to host in that class
 
553
    //
 
554
    if (wu_result.infeasible_count) {
 
555
        score += 1;
 
556
    }
 
557
 
 
558
    // Favor jobs that will run fast
 
559
    //
 
560
    score += bavp->host_usage.projected_flops/1e9;
 
561
 
 
562
    // match large jobs to fast hosts
 
563
    //
 
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;
 
567
        score -= diff*diff;
 
568
    }
 
569
 
 
570
    // TODO: If user has selected some apps but will accept jobs from others,
 
571
    // try to send them jobs from the selected apps
 
572
    //
 
573
 
 
574
    est_time = estimate_duration(wu, *bavp);
 
575
    disk_usage = wu.rsc_disk_bound;
 
576
    return true;
 
577
}