The flops estimation code has been hard for me to get my brain around.
 It's possible that the problem is somewhere in the change from separate
ATI and CUDA coprocessor support to a single GPU requirements structure.
I've attached my sched_customize.cpp.  I think I've made it compatible with
the stock version now, so I could check it in.  But I don't want to mess
anyone else up so I haven't.



On Wed, Jul 18, 2012 at 12:27 AM, Bernd Machenschalk <
[email protected]> wrote:

> Hi Eric!
>
> As the original author of that plan_class_spec code I have to admit that
> the flops scaling etc. is still something of a mystery to me, and although
> I changed it a few times I am pretty sure I didn't get this right.
> Apparently the latest rework by David didn't fix it either, although the
> latest version should use the functions provided in sched_customize.cpp and
> thus have more code in common with it.
>
> Could you send me (or point me to) SETIs sched_customize.cpp? I hope it
> could help me to understand and fix the problem.
>
> Best,
> Bernd
>
>
> On 14.07.12 18:32, Eric J Korpela wrote:
>
>> I've spent a few weeks in the SETI@home beta trying to move from some
>> highly customized plan classes to the plan_class_spec mechanism for some
>> OpenCL apps.  It's not working, and at this point I'm going to go back to
>> the highly customized plan classes even though they are difficult the keep
>> synced with changes to the source tree.
>>
>> The best I can figure out is that a lot of the scheduling and credit
>> mechanisms are having problems.  Many of the problems seem to have
>> something to do with flop scaling.  In sched_customize.cpp where
>> customizable GPU plan classes live, a flops scale is applied to the GPUs
>> processing rate.  In plan_class_sched, it's applied to the CPUs processing
>> rate even for GPU plans.   The "sample" plan_class_spec.xml file has
>> values
>> that would be suitable for the sched_customize method do not work for
>> plan_class_spec at all.  The scheduler itself is very confused about what
>> it means.  If you put a value of 10 for flops_scale in
>> plan_class_spec.xml,
>> it will multiply the cpu benchmarks by 10 to get the processing rate, but
>> then it does an incorrect calculation of processing rate based on PFC.
>>   Most GPUs seem to get estimated at about 100MFLOP from the bad PFC
>> calculation.  Credits granted for GPU work done come in about 1/500th of
>> what they should be.  Even beta testers get annoyed when the see 0.86
>> credits for something that should have been 700.
>>
>> Unfortunately there's not a a lot of source commonality between the two
>> mechanisms, so anything I learned writing plan classes is lost in
>> plan_class_spec.  So I'm giving up on fixing it myself.
>>
>> If you're planning to use the plan_class_spec mechanism, I would hold off
>> until it's fixed.
>> ______________________________**_________________
>> boinc_dev mailing list
>> [email protected]
>> http://lists.ssl.berkeley.edu/**mailman/listinfo/boinc_dev<http://lists.ssl.berkeley.edu/mailman/listinfo/boinc_dev>
>> To unsubscribe, visit the above URL and
>> (near bottom of page) enter your email address.
>>
>
>
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
//

// This file contains functions that can be customized to
// implement project-specific scheduling policies.
// The functions are:
//
// wu_is_infeasible_custom()
//      Decide whether host can run a job using a particular app version.
//      In addition it can:
//      - set the app version's resource usage and/or FLOPS rate estimate
//          (by assigning to bav.host_usage)
//      - modify command-line args
//          (by assigning to bav.host_usage.cmdline)
//      - set the job's FLOPS count
//          (by assigning to wu.rsc_fpops_est)
//
// app_plan()
//      Decide whether host can use an app version,
//      and if so what resources it will use
//
// app_plan_uses_gpu():
//      Which plan classes use GPUs
//
// JOB::get_score():
//      Determine the value of sending a particular job to host;
//      (used only by "matchmaker" scheduling)
//
// WARNING: if you modify this file, you must prevent it from
// being overwritten the next time you update BOINC source code.
// You can either:
// 1) write-protect this file, or
// 2) put this in a differently-named file and change the Makefile.am
//    (and write-protect that)
// In either case, put your version under source-code control, e.g. SVN

#include <string>

using std::string;

#include "str_util.h"
#include "util.h"

#include "sched_config.h"
#include "sched_main.h"
#include "sched_msgs.h"
#include "sched_send.h"
#include "sched_score.h"
#include "sched_shmem.h"
#include "sched_version.h"
#include "sched_customize.h"
#include "plan_class_spec.h"

GPU_REQUIREMENTS gpu_requirements[NPROC_TYPES];

bool wu_is_infeasible_custom(WORKUNIT& wu, APP& app, BEST_APP_VERSION& bav) {
#if 0
    // example: if WU name contains "_v1", don't use CUDA app
    // Note: this is slightly suboptimal.
    // If the host is able to accept both GPU and CPU jobs,
    // we'll skip this job rather than send it for the CPU.
    // Fixing this would require a big architectural change.
    //
    if (strstr(wu.name, "_v1") && bav.host_usage.ncudas) {
        return true;
    }
#endif
#if 0
    // example: for CUDA app, wu.batch is the minimum number of processors.
    // Don't send if #procs is less than this.
    //
    if (!strcmp(app.name, "foobar") && bav.host_usage.ncudas) {
        int n = g_request->coproc_cuda->prop.multiProcessorCount;
        if (n < wu.batch) {
           return true;
        }
    }
#endif
#if 0
    // example: if CUDA app and WU name contains ".vlar", don't send
    //
    if (bav.host_usage.ncudas) {
        if (strstr(wu.name, ".vlar")) {
            return true;
        }
    }
#endif
    return false;
}

#ifndef isnum
#define isnum(x) (((x)>='0') && ((x)<='9'))
#endif

#ifndef isnumorx
#define isnumorx(x) (isnum(x) || ((x=='X') || (x=='x')))
#endif

// the following is for an app that can use anywhere from 1 to 64 threads
//
static inline bool app_plan_mt(SCHEDULER_REQUEST&, HOST_USAGE& hu) {
    double ncpus = g_wreq->effective_ncpus;
        // number of usable CPUs, taking user prefs into account
    if (ncpus < 2) return false;
    int nthreads = (int)ncpus;
    if (nthreads > 64) nthreads = 64;
    hu.avg_ncpus = nthreads;
    hu.max_ncpus = nthreads;
    sprintf(hu.cmdline, "--nthreads %d", nthreads);
    hu.projected_flops = capped_host_fpops()*hu.avg_ncpus*.99;
        // the .99 ensures that on uniprocessors a sequential app
        // will be used in preferences to this
    hu.peak_flops = capped_host_fpops()*hu.avg_ncpus;
    if (config.debug_version_select) {
        log_messages.printf(MSG_NORMAL,
            "[version] Multi-thread app projected %.2fGS\n",
            hu.projected_flops/1e9
        );
    }
    return true;
}

static bool ati_check(COPROC_ATI& c, HOST_USAGE& hu,
    int min_driver_version,
    bool need_amd_libs,
    double min_ram,
    double ndevs,       // # of GPUs used; can be fractional
    double cpu_frac,    // fraction of FLOPS performed by CPU
    double flops_scale,
    int min_hd_model=0
) {
    if (c.version_num) {
        gpu_requirements[PROC_TYPE_AMD_GPU].update(min_driver_version, min_ram);
    }

    if (min_hd_model) {
        char *p=strcasestr(c.name,"hd");
	if (p) {
	    p+=2;
	    while (p && !isnum(*p)) p++;
	    char modelnum[64];
	    int i=0;
	    while ((i<63) && p[i] && isnumorx(p[i])) {
	        modelnum[i]=p[i];
	        if ((modelnum[i]=='x') || (modelnum[i]=='X')) {
	          modelnum[i]='0';
                }
	        i++;
	    }
	    modelnum[i]=0;
	    i=atoi(modelnum);
            if (i<min_hd_model) {
                if (config.debug_version_select) {
                    log_messages.printf(MSG_NORMAL,
                        "[version] Requires ATI HD%4d+.  Found HD%4d\n",
			min_hd_model, i
                    );
                }
	        return false;
	    }
	}
    }


    if (need_amd_libs) {
        if (!c.amdrt_detected) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,"[version] AMD run time libraries not found\n");
            }
            return false;
        }
    } else {
        if (!c.atirt_detected) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,"[version] ATI run time libraries not found\n");
            }
            return false;
        }
    }
    if (c.version_num < min_driver_version) {
        if (config.debug_version_select) {
            int app_major=min_driver_version/10000000;
            int app_minor=(min_driver_version%10000000)/10000;
            int app_rev=(min_driver_version%10000);
            int dev_major=c.version_num/10000000;
            int dev_minor=(c.version_num%10000000)/10000;
            int dev_rev=(c.version_num%10000);
            log_messages.printf(MSG_NORMAL,"[version] Bad display driver revision %d.%d.%d<%d.%d.%d.\n",
                dev_major,dev_minor,dev_rev,app_major,app_minor,app_rev 
            );
        }
        return false;
    }
    if (c.available_ram < min_ram) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,"[version] Insufficient GPU RAM %f>%f.\n",
                min_ram, c.available_ram
            );
        }
        return false;
    }

    hu.gpu_ram = min_ram;
    hu.proc_type = PROC_TYPE_AMD_GPU;
    hu.gpu_usage = ndevs;

    coproc_perf(
        capped_host_fpops(),
        flops_scale * hu.gpu_usage*c.peak_flops,
        cpu_frac,
        hu.projected_flops,
        hu.avg_ncpus
    );
    if (config.debug_version_select) {
      log_messages.printf(MSG_NORMAL,"[version] [ati_check] coproc_perf(capped_host_fpops=%fG,flops_scale=%f*gpu_usage=%f*peak_flops=%fG,cpu_frac=%f,projected_flops=%fG,avg_ncpus=%f)\n",capped_host_fpops()/1e9,flops_scale,hu.gpu_usage,c.peak_flops/1e9,cpu_frac,hu.projected_flops/1e9,hu.avg_ncpus);
    }
    
    hu.peak_flops = hu.gpu_usage*c.peak_flops + hu.avg_ncpus*capped_host_fpops();
    hu.max_ncpus = hu.avg_ncpus;
    return true;
}

#define ATI_MIN_RAM 222*MEGA
static inline bool app_plan_ati(
    SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
) {
    COPROC_ATI& c = sreq.coprocs.ati;
    if (!c.count) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,"[version] Host has no ATI GPUs\n");
        }
        return false;
    }

    if (!strcmp(plan_class, "ati")) {
        if (!ati_check(c, hu,
            ati_version_int(1, 0, 0),
            true,
            ATI_MIN_RAM,
            1,
            .01,
            .20
        )) {
            return false;
        }
    }

    if (!strcmp(plan_class, "ati13amd")) {
        if (!ati_check(c, hu,
            ati_version_int(1, 3, 0),
            true,
            ATI_MIN_RAM,
            1, .01,
            .21
        )) {
            return false;
        }
    }

    if (!strcmp(plan_class, "ati13ati")) {
        if (!ati_check(c, hu,
            ati_version_int(1, 3, 186),
            false,
            ATI_MIN_RAM,
            1, .01,
            .22
        )) {
            return false;
        }
    }

    if (!strcmp(plan_class, "ati14")) {
        if (!ati_check(c, hu,
            ati_version_int(1, 4, 0),
            false,
            ATI_MIN_RAM,
            1, .01,
            .23
        )) {
            return false;
        }
    }

    if (!strcmp(plan_class, "ati_opencl_100")) {
        if (!ati_check(c, hu,
            ati_version_int(1, 4, 1386),
            false,
            ATI_MIN_RAM,
            1, .01,
            .14,
	    4600
        )) {
            return false;
        }
    }

    if (config.debug_version_select) {
        log_messages.printf(MSG_NORMAL,
            "[version] %s ATI app projected %.2fG peak %.2fG %.3f CPUs\n",
            plan_class,
            hu.projected_flops/1e9,
            hu.peak_flops/1e9,
            hu.avg_ncpus
        );
    }
    return true;
}

#define CUDA_MIN_DRIVER_VERSION             17700
#define CUDA23_MIN_CUDA_VERSION             2030
#define CUDA23_MIN_DRIVER_VERSION           19038
#define CUDA3_MIN_CUDA_VERSION              3000
#define CUDA3_MIN_DRIVER_VERSION            19500
#define CUDA_OPENCL_MIN_DRIVER_VERSION      19713
#define CUDA_OPENCL_101_MIN_DRIVER_VERSION  28013
#define CUDA_OPENCL_101_MIN_DRIVER_VERSION  28013

static bool cuda_check(COPROC_NVIDIA& c, HOST_USAGE& hu,
    int min_cc, int max_cc,
    int min_cuda_version, int min_driver_version,
    double min_ram,
    double ndevs,       // # of GPUs used; can be fractional
    double cpu_frac,    // fraction of FLOPS performed by CPU
    double flops_scale
) {
    int cc = c.prop.major*100 + c.prop.minor;
    if (min_cc && (cc < min_cc)) {
        log_messages.printf(MSG_NORMAL,"[version] App requires compute capability > %d.%d (has %d.%d).\n",
            min_cc/100,min_cc%100,
            c.prop.major,c.prop.minor
        );
        return false;
    }

    if (max_cc && cc >= max_cc) { 
        log_messages.printf(MSG_NORMAL,"[version] App requires compute capability <= %d.%d (has %d.%d).\n",
            max_cc/100,max_cc%100,
            c.prop.major,c.prop.minor
        );
        return false;
    }

    if (c.display_driver_version) {
        gpu_requirements[PROC_TYPE_NVIDIA_GPU].update(min_driver_version, min_ram);
    }

    // Old BOINC clients report display driver version;
    // newer ones report CUDA RT version.
    // Some Linux doesn't return either.
    //
    if (!c.cuda_version && !c.display_driver_version) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,"[version] Client did not provide cuda or driver version.\n");
        }
        return false;
    }
    if (c.cuda_version) {
        if (min_cuda_version && (c.cuda_version < min_cuda_version)) {
            if (config.debug_version_select) {
                double app_version=(double)(min_cuda_version/1000)+(double)(min_cuda_version%100)/100.0;
                double client_version=(double)(c.cuda_version/1000)+(double)(c.cuda_version%100)/100.0;
                log_messages.printf(MSG_NORMAL,"[version] Bad CUDA version %f>%f.\n",
                    app_version, client_version
                );
            }
            return false;
        }
    }
    if (c.display_driver_version) {
        if (min_driver_version && (c.display_driver_version < min_driver_version)) {
            if (config.debug_version_select) {
              double app_version=(double)(min_driver_version)/100.0;
              double client_version=(double)(c.display_driver_version)/100.0;
              log_messages.printf(MSG_NORMAL,"[version] Bad display driver revision %f>%f.\n",
                 app_version, client_version
                 );
            }
            return false;
        }
    }
    if (c.available_ram < min_ram) {
        if (config.debug_version_select) {
            log_messages.printf(MSG_NORMAL,"[version] Insufficient GPU RAM %f>%f.\n",
                min_ram, c.available_ram
            );
        }
        return false;
    }

    hu.gpu_ram = min_ram;
    hu.proc_type = PROC_TYPE_NVIDIA_GPU;
    hu.gpu_usage = ndevs;

    coproc_perf(
        capped_host_fpops(),
        flops_scale * hu.gpu_usage*c.peak_flops,
        cpu_frac,
        hu.projected_flops,
        hu.avg_ncpus
    );
    if (config.debug_version_select) {
      log_messages.printf(MSG_NORMAL,"[version] [cuda_check] coproc_perf(capped_host_fpops=%fG,flops_scale=%f*gpu_usage=%f*peak_flops=%fG,cpu_frac=%f,projected_flops=%fG,avg_ncpus=%f)\n",capped_host_fpops()/1e9,flops_scale,hu.gpu_usage,c.peak_flops/1e9,cpu_frac,hu.projected_flops/1e9,hu.avg_ncpus);
    }
    hu.peak_flops = hu.gpu_usage*c.peak_flops + hu.avg_ncpus*capped_host_fpops();
    hu.max_ncpus = hu.avg_ncpus;
    return true;
}

// the following is for an app that uses an NVIDIA GPU
//
static inline bool app_plan_cuda(
    SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
) {
    COPROC_NVIDIA& c = sreq.coprocs.nvidia;
    if (!c.count) {
        log_messages.printf(MSG_NORMAL,"[version] Host has no NVIDIA GPUs.\n");
        return false;
    }

    // Macs require 6.10.28
    //
    if (strstr(sreq.host.os_name, "Darwin") && (sreq.core_client_version < 61028)) {
        log_messages.printf(MSG_NORMAL,"[version] CUDA on MacOS requires BOINC 6.10.28 or higher.\n");
        return false;
    }

    // for CUDA 2.3, we need to check the CUDA RT version.
    // Old BOINC clients report display driver version;
    // newer ones report CUDA RT version
    //
    if (!strcmp(plan_class, "cuda_opencl_100")) {
        if (!cuda_check(c, hu,
	    100, 0,
            0,CUDA_OPENCL_MIN_DRIVER_VERSION,
            222*MEGA,
	    1,
	    .01,
	    0.14
	)) {
	    return false;
	}
    } else if (!strcmp(plan_class, "cuda_opencl_101")) {
        if (!cuda_check(c, hu,
	    200, 0,
            0,CUDA_OPENCL_101_MIN_DRIVER_VERSION,
            222*MEGA,
	    1,
	    .01,
	    0.14
	)) {
	    return false;
	}
    } else if (!strcmp(plan_class, "cuda_fermi")) {
        if (!cuda_check(c, hu,
            200, 0,
            CUDA3_MIN_CUDA_VERSION, CUDA3_MIN_DRIVER_VERSION,
            384*MEGA,
            1,
            .01,
            .22
        )) {
            return false;
        }
    } else if (!strcmp(plan_class, "cuda23")) {
        if (!cuda_check(c, hu,
            100,
            200,    // change to zero if app is compiled to byte code
            CUDA23_MIN_CUDA_VERSION, CUDA23_MIN_DRIVER_VERSION,
            384*MEGA,
            1,
            .01,
            .21
        )) {
            return false;
        }
    } else if (!strcmp(plan_class, "cuda")) {
        if (!cuda_check(c, hu,
            100,
            200,    // change to zero if app is compiled to byte code
            0, CUDA_MIN_DRIVER_VERSION,
            222*MEGA,
            1,
            .01,
            .20
        )) {
            return false;
        }
    } else {
        log_messages.printf(MSG_CRITICAL,
            "UNKNOWN PLAN CLASS %s\n", plan_class
        );
        return false;
    }

    if (config.debug_version_select) {
        log_messages.printf(MSG_NORMAL,
            "[version] %s app projected %.2fG peak %.2fG %.3f CPUs\n",
            plan_class,
            hu.projected_flops/1e9,
            hu.peak_flops/1e9,
            hu.avg_ncpus
        );
    }
    return true;
}

// The following is for a non-CPU-intensive application.
// Say that we'll use 1% of a CPU.
// This will cause the client (6.7+) to run it at non-idle priority
//
static inline bool app_plan_nci(SCHEDULER_REQUEST&, HOST_USAGE& hu) {
    hu.avg_ncpus = .01;
    hu.max_ncpus = .01;
    hu.projected_flops = capped_host_fpops()*1.01;
        // The *1.01 is needed to ensure that we'll send this app
        // version rather than a non-plan-class one
    hu.peak_flops = capped_host_fpops()*.01;
    return true;
}

// the following is for an app version that requires a processor with SSE3,
// and will run 10% faster than the non-SSE3 version
//
static inline bool app_plan_sse3(
    SCHEDULER_REQUEST& sreq, HOST_USAGE& hu
) {
    downcase_string(sreq.host.p_features);
    if (!strstr(sreq.host.p_features, "sse3")) {
        // Pre-6.x clients report CPU features in p_model
        //
        if (!strstr(sreq.host.p_model, "sse3")) {
            //add_no_work_message("Your CPU lacks SSE3");
            return false;
        }
    }
    hu.avg_ncpus = 1;
    hu.max_ncpus = 1;
    hu.projected_flops = 1.1*capped_host_fpops();
    hu.peak_flops = capped_host_fpops();
    return true;
}

static inline bool opencl_check(
    COPROC& cp, HOST_USAGE& hu,
    int min_opencl_device_version,
    double min_global_mem_size,
    double ndevs,
    double cpu_frac,
    double flops_scale
) {
    if (cp.opencl_prop.opencl_device_version_int < min_opencl_device_version) {
        return false;
    }
    if (cp.opencl_prop.global_mem_size < min_global_mem_size) {
        return false;
    }

    hu.gpu_ram = min_global_mem_size;
    if (!strcmp(cp.type, "NVIDIA")) {
        hu.proc_type = PROC_TYPE_NVIDIA_GPU;
        hu.gpu_usage = ndevs;
    } else if (!strcmp(cp.type, "ATI")) {
        hu.proc_type = PROC_TYPE_AMD_GPU;
        hu.gpu_usage = ndevs;
    }

    coproc_perf(
        capped_host_fpops(),
        flops_scale * ndevs * cp.peak_flops,
        cpu_frac,
        hu.projected_flops,
        hu.avg_ncpus
    );
    if (config.debug_version_select) {
      log_messages.printf(MSG_NORMAL,"[version] [opencl_check] coproc_perf(capped_host_fpops=%fG,flops_scale=%f*gpu_usage=%f*peak_flops=%fG,cpu_frac=%f,projected_flops=%fG,avg_ncpus=%f)\n",capped_host_fpops()/1e9,flops_scale,hu.gpu_usage,cp.peak_flops/1e9,cpu_frac,hu.projected_flops/1e9,hu.avg_ncpus);
    }
    hu.peak_flops = ndevs*cp.peak_flops + hu.avg_ncpus*capped_host_fpops();
    hu.max_ncpus = hu.avg_ncpus;
    return true;
}


static inline bool app_plan_opencl(
    SCHEDULER_REQUEST& sreq, const char* plan_class, HOST_USAGE& hu
) {
    const char *p=plan_class+strlen(plan_class);
    while (isnum(p[-1])) {
	p--;
    }
    int ver=atoi(p);
    if (config.debug_version_select) {
        log_messages.printf(MSG_NORMAL,
            "[version] plan_class %s uses OpenCl version %d\n",
            plan_class,
	    ver
        );
    }
    if (strstr(plan_class, "nvidia")) {
        COPROC_NVIDIA& c = sreq.coprocs.nvidia;
        if (!c.count) return false;
        if (!c.have_opencl) return false;
        if (strstr(plan_class,"opencl_nvidia") == plan_class) {
            return opencl_check(
                c, hu,
                ver,
                222*MEGA,
                1,
                .01,
                .14
            );
        } else {
            log_messages.printf(MSG_CRITICAL,
                "Unknown plan class: %s\n", plan_class
            );
            return false;
        }
    } else if (strstr(plan_class, "ati")) {
        COPROC_ATI& c = sreq.coprocs.ati;
        if (!c.count) return false;
        if (!c.have_opencl) return false;
        if (strstr(plan_class,"opencl_ati") == plan_class) {
            return opencl_check(
                c, hu,
                ver,
                222*MEGA,
                1,
                .01,
                .14
            );
        } else {
            log_messages.printf(MSG_CRITICAL,
                "Unknown plan class: %s\n", plan_class
            );
            return false;
        }

    // maybe add a clause for multicore CPU

    } else {
        log_messages.printf(MSG_CRITICAL,
            "Unknown plan class: %s\n", plan_class
        );
        return false;
    }
}

// handles vbox_[32|64][_mt]
// "mt" is tailored to the needs of CERN:
// use 1 or 2 CPUs

static inline bool app_plan_vbox(
    SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu
) {
    bool can_use_multicore = true;

    // host must run 7.0+ client
    //
    if (sreq.core_client_major_version < 7) {
        add_no_work_message("BOINC client 7.0+ required for Virtualbox jobs");
        return false;
    }

    // host must have VirtualBox 3.2 or later
    //
    if (strlen(sreq.host.virtualbox_version) == 0) {
        add_no_work_message("VirtualBox is not installed");
        return false;
    }
    int n, maj, min, rel;
    n = sscanf(sreq.host.virtualbox_version, "%d.%d.%d", &maj, &min, &rel);
    if ((n != 3) || (maj < 3) || (maj == 3 and min < 2)) {
        add_no_work_message("VirtualBox version 3.2 or later is required");
        return false;
    }

    // host must have VM acceleration in order to run multi-core jobs
    //
    if (strstr(plan_class, "mt")) {
        if ((!strstr(sreq.host.p_features, "vmx") && !strstr(sreq.host.p_features, "svm"))
            || sreq.host.p_vm_extensions_disabled
        ) {
            can_use_multicore = false;
        }
    }

    // only send the version for host's primary platform.
    // A Win64 host can't run a 32-bit VM app:
    // it will look in the 32-bit half of the registry and fail
    //
    PLATFORM* p = g_request->platforms.list[0];
    if (is_64b_platform(p->name)) {
        if (!strstr(plan_class, "64")) return false;
    } else {
        if (strstr(plan_class, "64")) return false;
    }

    double flops_scale = 1;
    hu.avg_ncpus = 1;
    hu.max_ncpus = 1;
    if (strstr(plan_class, "mt")) {
        if (can_use_multicore) {
            // Use number of usable CPUs, taking user prefs into account
            double ncpus = g_wreq->effective_ncpus;
            // CernVM on average uses between 25%-50% of a second core
            // Total on a dual-core machine is between 65%-75%
            if (ncpus > 1.5) ncpus = 1.5;
            hu.avg_ncpus = ncpus;
            hu.max_ncpus = 2.0;
            sprintf(hu.cmdline, "--nthreads %f", ncpus);
        }
        // use the non-mt version rather than the mt version with 1 CPU
        //
        flops_scale = .99;
    }
    hu.projected_flops = flops_scale * capped_host_fpops()*hu.avg_ncpus;
    hu.peak_flops = capped_host_fpops()*hu.max_ncpus;
    if (config.debug_version_select) {
        log_messages.printf(MSG_NORMAL,
            "[version] %s app projected %.2fG\n",
            plan_class, hu.projected_flops/1e9
        );
    }
    return true;
}

PLAN_CLASS_SPECS plan_class_specs;

// app planning function.
// See http://boinc.berkeley.edu/trac/wiki/AppPlan
//
bool app_plan(SCHEDULER_REQUEST& sreq, char* plan_class, HOST_USAGE& hu) {
    char buf[256];
    static bool check_plan_class_spec = true;
    static bool have_plan_class_spec = false;
    static bool bad_plan_class_spec = false;

    if (config.debug_version_select) {
        log_messages.printf(MSG_NORMAL,
            "[version] Checking plan class '%s'\n", plan_class
        );
    }

    if (check_plan_class_spec) {
        check_plan_class_spec = false;
        strcpy(buf, config.project_dir);
        strcat(buf, "/plan_class_spec.xml");
        int retval = plan_class_specs.parse_file(buf);
        if (retval == ERR_FOPEN) {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] Couldn't open plan class spec file '%s'\n", buf
                );
            }
            have_plan_class_spec = false;
        } else if (retval) {
            log_messages.printf(MSG_CRITICAL,
                "Error parsing plan class spec file '%s'\n", buf
            );
            bad_plan_class_spec = true;
        } else {
            if (config.debug_version_select) {
                log_messages.printf(MSG_NORMAL,
                    "[version] reading plan classes from file '%s'\n", buf
                );
            }
            have_plan_class_spec = true;
        }
    }
    if (bad_plan_class_spec) {
        return false;
    }
    if (have_plan_class_spec) {
        return plan_class_specs.check(sreq, plan_class, hu);
    }

    if (!strcmp(plan_class, "mt")) {
        return app_plan_mt(sreq, hu);
    } else if (strstr(plan_class, "opencl") == plan_class) {
        return app_plan_opencl(sreq, plan_class, hu);
    } else if (strstr(plan_class, "ati") == plan_class) {
        return app_plan_ati(sreq, plan_class, hu);
    } else if (strstr(plan_class, "cuda")) {
        return app_plan_cuda(sreq, plan_class, hu);
    } else if (!strcmp(plan_class, "nci")) {
        return app_plan_nci(sreq, hu);
    } else if (!strcmp(plan_class, "sse3")) {
        return app_plan_sse3(sreq, hu);
    } else if (strstr(plan_class, "vbox")) {
        return app_plan_vbox(sreq, plan_class, hu);
    }
    log_messages.printf(MSG_CRITICAL,
        "Unknown plan class: %s\n", plan_class
    );
    return false;
}

// compute a "score" for sending this job to this host.
// Return false if the WU is infeasible.
// Otherwise set est_time and disk_usage.
//
bool JOB::get_score() {
    WORKUNIT wu;
    int retval;

    WU_RESULT& wu_result = ssp->wu_results[index];
    wu = wu_result.workunit;
    app = ssp->lookup_app(wu.appid);

    score = 0;

    // Find the best app version to use.
    //
    bavp = get_app_version(wu, true, false);
    if (!bavp) return false;

    retval = wu_is_infeasible_fast(
        wu, wu_result.res_server_state, wu_result.res_priority,
        wu_result.res_report_deadline,
        *app, *bavp
    );
    if (retval) {
        if (config.debug_send) {
            log_messages.printf(MSG_NORMAL,
                "[send] [HOST#%d] [WU#%d %s] WU is infeasible: %s\n",
                g_reply->host.id, wu.id, wu.name, infeasible_string(retval)
            );
        }
        return false;
    }

    score = 1;

#if 0
    // example: for CUDA app, wu.batch is the minimum number of processors.
    // add min/actual to score
    // (this favors sending jobs that need lots of procs to GPUs that have them)
    // IF YOU USE THIS, USE THE PART IN wu_is_infeasible_custom() ALSO
    //
    if (!strcmp(app->name, "foobar") && bavp->host_usage.ncudas) {
        int n = g_request->coproc_cuda->prop.multiProcessorCount;
        score += ((double)wu.batch)/n;
    }
#endif

    // check if user has selected apps,
    // and send beta work to beta users
    //
    if (app->beta && !config.distinct_beta_apps) {
        if (g_wreq->allow_beta_work) {
            score += 1;
        } else {
            return false;
        }
    } else {
        if (app_not_selected(wu)) {
            if (!g_wreq->allow_non_preferred_apps) {
                return false;
            } else {
            // Allow work to be sent, but it will not get a bump in its score
            }
        } else {
            score += 1;
        }
    }
            
    // if job needs to get done fast, send to fast/reliable host
    //
    if (bavp->reliable && (wu_result.need_reliable)) {
        score += 1;
    }
    
    // if job already committed to an HR class,
    // try to send to host in that class
    //
    if (wu_result.infeasible_count) {
        score += 1;
    }

    // Favor jobs that will run fast
    //
    score += bavp->host_usage.projected_flops/1e9;

    // match large jobs to fast hosts
    //
    if (config.job_size_matching) {
        double host_stdev = (capped_host_fpops() - ssp->perf_info.host_fpops_mean)/ ssp->perf_info.host_fpops_stddev;
        double diff = host_stdev - wu_result.fpops_size;
        score -= diff*diff;
    }

    // TODO: If user has selected some apps but will accept jobs from others,
    // try to send them jobs from the selected apps
    //

    est_time = estimate_duration(wu, *bavp);
    disk_usage = wu.rsc_disk_bound;
    return true;
}

void handle_file_xfer_results() {
    for (unsigned int i=0; i<g_request->file_xfer_results.size(); i++) {
        RESULT& r = g_request->file_xfer_results[i];
        log_messages.printf(MSG_NORMAL,
            "completed file xfer %s\n", r.name
        );
        g_reply->result_acks.push_back(string(r.name));
    }
}
_______________________________________________
boinc_dev mailing list
[email protected]
http://lists.ssl.berkeley.edu/mailman/listinfo/boinc_dev
To unsubscribe, visit the above URL and
(near bottom of page) enter your email address.

Reply via email to