diff --git a/checkin_notes b/checkin_notes index b21a4d220e..39198c7546 100755 --- a/checkin_notes +++ b/checkin_notes @@ -9901,3 +9901,9 @@ David 6 Sept 2006 client/ client_types.C,h + +David 6 Sept 2006 + - added program to test rr_simulation() + + client/ + rrsim_test.C diff --git a/client/rrsim_test.C b/client/rrsim_test.C new file mode 100644 index 0000000000..9cad08e326 --- /dev/null +++ b/client/rrsim_test.C @@ -0,0 +1,429 @@ +// This is a test framework for the rr_simulation() function. +// To use it: +// - cut and paste the current code from cpu_sched.C (see below) +// - edit main() to set up your test case + +#include +#include + +using std::vector; + +#define CPU_PESSIMISM_FACTOR 1.0 +#define SECONDS_PER_DAY 86400 + +struct RESULT; + +struct PROJECT { + char name[256]; + double resource_share; + bool non_cpu_intensive; + vectoractive; + vectorpending; + double cpu_shortfall; + double rrsim_proc_rate; + int rr_sim_deadlines_missed; + PROJECT(char* n, double rs) { + strcpy(name, n); + resource_share = rs; + non_cpu_intensive = false; + } + char* get_project_name() { + return name; + } + void set_rrsim_proc_rate(double); +}; + +struct RESULT { + char name[256]; + double ectr; + double estimated_cpu_time_remaining() { + return ectr; + } + double rrsim_finish_delay; + double rrsim_cpu_left; + double report_deadline; + bool rr_sim_misses_deadline; + bool last_rr_sim_missed_deadline; + PROJECT* project; + RESULT(PROJECT* p, char* n, double e, double rd) { + project = p; + strcpy(name, n); + ectr = e; + report_deadline = rd; + } + bool nearly_runnable() { + return true; + } + double computation_deadline(); +}; + +struct FLAGS { + bool rr_simulation; +}; + +FLAGS log_flags; + +struct PREFS { + double work_buf_min_days; + double cpu_scheduling_period_minutes; +}; + +struct CLIENT_STATE { + double nearly_runnable_resource_share(); + double total_resource_share(); + double now; + int ncpus; + vectorresults; + vectorprojects; + PREFS global_prefs; + double cpu_shortfall; + double overall_cpu_frac() { + return 1; + } + bool rr_simulation(); + double work_buf_min() { + return global_prefs.work_buf_min_days*86400; + } +}; + +struct CLIENT_STATE gstate; + +double CLIENT_STATE::nearly_runnable_resource_share() { + double x=0; + for (unsigned int i=0; iresource_share; + } + return x; +} + +double CLIENT_STATE::total_resource_share() { + return nearly_runnable_resource_share(); +} + +#define MSG_INFO 0 + +void msg_printf(PROJECT* p, int, const char* fmt, ...) { + char buf[8192]; + va_list ap; + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + printf("%s: %s\n", p?p->name:"BOINC", buf); +} + +///////////////////// CUT AND PASTE FROM CPU_SCHED.C ////////////// + +double RESULT::computation_deadline() { + return report_deadline - ( + gstate.global_prefs.work_buf_min_days * SECONDS_PER_DAY + // Seconds that the host will not be connected to the Internet + + gstate.global_prefs.cpu_scheduling_period_minutes * 60 + // Seconds that the CPU may be busy with some other result + + SECONDS_PER_DAY + // Deadline cusion + ); +} + +void PROJECT::set_rrsim_proc_rate(double rrs) { + int nactive = (int)active.size(); + if (nactive == 0) return; + double x; + + if (rrs) { + x = resource_share/rrs; + } else { + x = 1; // pathological case; maybe should be 1/# runnable projects + } + + // if this project has fewer active results than CPUs, + // scale up its share to reflect this + // + if (nactive < gstate.ncpus) { + x *= ((double)gstate.ncpus)/nactive; + } + + // But its rate on a given CPU can't exceed 1 + // + if (x>1) { + x = 1; + } + rrsim_proc_rate = x*gstate.overall_cpu_frac(); + if (log_flags.rr_simulation) { + msg_printf(this, MSG_INFO, + "set_rrsim_proc_rate: %f (rrs %f, rs %f, nactive %d, ocf %f", + rrsim_proc_rate, rrs, resource_share, nactive, gstate.overall_cpu_frac() + ); + } +} +bool CLIENT_STATE::rr_simulation() { + double rrs = nearly_runnable_resource_share(); + double trs = total_resource_share(); + PROJECT* p, *pbest; + RESULT* rp, *rpbest; + vector active; + unsigned int i; + double x; + vector::iterator it; + bool rval = false; + + if (log_flags.rr_simulation) { + msg_printf(0, MSG_INFO, + "rr_sim start: work_buf_min %f rrs %f trs %f", + work_buf_min(), rrs, trs + ); + } + + // Initialize result lists for each project: + // "active" is what's currently running (in the simulation) + // "pending" is what's queued + // + for (i=0; iactive.clear(); + p->pending.clear(); + p->rr_sim_deadlines_missed = 0; + p->cpu_shortfall = 0; + } + + for (i=0; inearly_runnable()) continue; + if (rp->project->non_cpu_intensive) continue; + rp->rrsim_cpu_left = rp->estimated_cpu_time_remaining(); + p = rp->project; + if (p->active.size() < (unsigned int)ncpus) { + active.push_back(rp); + p->active.push_back(rp); + } else { + p->pending.push_back(rp); + } + rp->last_rr_sim_missed_deadline = rp->rr_sim_misses_deadline; + rp->rr_sim_misses_deadline = false; + } + + for (i=0; iset_rrsim_proc_rate(rrs); + // if there are no results for a project, + // the shortfall is its entire share. + // + if (!p->active.size()) { + double rsf = trs ? p->resource_share/trs : 1; + p->cpu_shortfall = work_buf_min() * overall_cpu_frac() * ncpus * rsf; + if (log_flags.rr_simulation) { + msg_printf(p, MSG_INFO, + "no results; shortfall %f wbm %f ocf %f rsf %f", + p->cpu_shortfall, work_buf_min(), overall_cpu_frac(), rsf + ); + } + } + } + + double buf_end = now + work_buf_min(); + + // Simulation loop. Keep going until work done + // + double sim_now = now; + cpu_shortfall = 0; + while (active.size()) { + + // compute finish times and see which result finishes first + // + rpbest = NULL; + for (i=0; iproject; + rp->rrsim_finish_delay = rp->rrsim_cpu_left/p->rrsim_proc_rate; + if (!rpbest || rp->rrsim_finish_delay < rpbest->rrsim_finish_delay) { + rpbest = rp; + } + } + + pbest = rpbest->project; + + if (log_flags.rr_simulation) { + msg_printf(pbest, MSG_INFO, + "rr_sim: result %s finishes after %f (%f/%f)", + rpbest->name, rpbest->rrsim_finish_delay, rpbest->rrsim_cpu_left, pbest->rrsim_proc_rate + ); + } + + // "rpbest" is first result to finish. Does it miss its deadline? + // + double diff = sim_now + rpbest->rrsim_finish_delay - ((rpbest->computation_deadline()-now)*CPU_PESSIMISM_FACTOR + now); + if (diff > 0) { + rpbest->rr_sim_misses_deadline = true; + pbest->rr_sim_deadlines_missed++; + rval = true; + if (log_flags.rr_simulation) { + msg_printf(0, MSG_INFO, + "rr_sim: result %s misses deadline by %f", + rpbest->name, diff + ); + } + } + + int last_active_size = active.size(); + int last_proj_active_size = pbest->active.size(); + + // remove *rpbest from active set, + // and adjust CPU time left for other results + // + it = active.begin(); + while (it != active.end()) { + rp = *it; + if (rp == rpbest) { + it = active.erase(it); + } else { + x = rp->project->rrsim_proc_rate*rpbest->rrsim_finish_delay; + rp->rrsim_cpu_left -= x; + it++; + } + } + + // remove *rpbest from its project's active set + // + it = pbest->active.begin(); + while (it != pbest->active.end()) { + rp = *it; + if (rp == rpbest) { + it = pbest->active.erase(it); + } else { + it++; + } + } + + // If project has more results, add one to active set. + // + if (pbest->pending.size()) { + rp = pbest->pending[0]; + pbest->pending.erase(pbest->pending.begin()); + active.push_back(rp); + pbest->active.push_back(rp); + } + + // If all work done for a project, subtract that project's share + // and recompute processing rates + // + if (pbest->active.size() == 0) { + rrs -= pbest->resource_share; + if (log_flags.rr_simulation) { + msg_printf(pbest, MSG_INFO, + "rr_sim: decr rrs by %f, new value %f", + pbest->resource_share, rrs + ); + } + for (i=0; iset_rrsim_proc_rate(rrs); + } + } + + // increment CPU shortfalls if necessary + // + if (sim_now < buf_end) { + double end_time = sim_now + rpbest->rrsim_finish_delay; + if (end_time > buf_end) end_time = buf_end; + double d_time = end_time - sim_now; + int nidle_cpus = ncpus - last_active_size; + if (nidle_cpus<0) nidle_cpus = 0; + if (nidle_cpus > 0) cpu_shortfall += d_time*nidle_cpus; + + double rsf = trs?pbest->resource_share/trs:1; + double proj_cpu_share = ncpus*rsf; + + if (last_proj_active_size < proj_cpu_share) { + pbest->cpu_shortfall += d_time*(proj_cpu_share - last_proj_active_size); + if (log_flags.rr_simulation) { + msg_printf(pbest, MSG_INFO, + "rr_sim: new shortfall %f d_time %f proj_cpu_share %f lpas %d", + pbest->cpu_shortfall, d_time, proj_cpu_share, last_proj_active_size + ); + } + } + + if (end_time < buf_end) { + d_time = buf_end - end_time; + // if this is the last result for this project, account for the tail + if (!pbest->active.size()) { + pbest->cpu_shortfall += d_time * proj_cpu_share; + if (log_flags.rr_simulation) { + msg_printf(pbest, MSG_INFO, "rr_sim proj out of work; shortfall %f d %f pcs %f", + pbest->cpu_shortfall, d_time, proj_cpu_share + ); + } + } + } + if (log_flags.rr_simulation) { + msg_printf(0, MSG_INFO, + "rr_sim total: idle cpus %d, last active %d, active %d, shortfall %f", + nidle_cpus, last_active_size, (int)active.size(), cpu_shortfall + + ); + msg_printf(0, MSG_INFO, + "rr_sim proj %s: last active %d, active %d, shortfall %f", + pbest->get_project_name(), last_proj_active_size, (int)pbest->active.size(), + pbest->cpu_shortfall + ); + } + } + + sim_now += rpbest->rrsim_finish_delay; + } + + if (sim_now < buf_end) { + cpu_shortfall += (buf_end - sim_now) * ncpus; + } + + if (log_flags.rr_simulation) { + for (i=0; icpu_shortfall) { + msg_printf(p, MSG_INFO, + "rr_sim: shortfall %f\n", p->cpu_shortfall + ); + } + } + msg_printf(NULL, MSG_INFO, + "rr_simulation: end; returning %s; total shortfall %f\n", + rval?"true":"false", + cpu_shortfall + ); + } + return rval; +} + +////////////////////// END CUT AND PASTE //////////////// + +int main() { + PROJECT* p; + RESULT* r; + log_flags.rr_simulation = true; + + gstate.global_prefs.work_buf_min_days = 1; + gstate.global_prefs.cpu_scheduling_period_minutes = 60; + gstate.ncpus = 1; + gstate.now = 0; + + p = new PROJECT("project A", 33.); + gstate.projects.push_back(p); + + r = new RESULT(p, "result 1", 9, 1e6); + gstate.results.push_back(r); + r = new RESULT(p, "result 2", 9, 1e6); + gstate.results.push_back(r); + r = new RESULT(p, "result 3", 9, 1e6); + gstate.results.push_back(r); + + p = new PROJECT("project B", 33.); + gstate.projects.push_back(p); + r = new RESULT(p, "result 4", 20, 1e6); + gstate.results.push_back(r); + + p = new PROJECT("project C", 33.); + gstate.projects.push_back(p); + r = new RESULT(p, "result 5", 30, 1e6); + gstate.results.push_back(r); + + gstate.rr_simulation(); +}