mirror of https://github.com/BOINC/boinc.git
839 lines
25 KiB
C++
839 lines
25 KiB
C++
// 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/>.
|
|
|
|
// BOINC client simulator.
|
|
//
|
|
// usage:
|
|
// sim [--duration x] [--delta x] [--dirs dir ...]
|
|
// duration = simulation duration (default 86400)
|
|
// delta = simulation time step (default 10)
|
|
//
|
|
// If no dirs are specified:
|
|
// reads input files
|
|
// sim_projects.xml, sim_host.xml, sim_prefs.xml, cc_config.xml
|
|
// and does simulation, generating output files
|
|
// sim_log.txt, sim_out.html
|
|
//
|
|
// If dirs are specified, chdir into each directory in sequence,
|
|
// do the above for each one, and write summary info to stdout
|
|
|
|
#ifdef _MSC_VER
|
|
#define chdir _chdir
|
|
#endif
|
|
|
|
#include "error_numbers.h"
|
|
#include "str_util.h"
|
|
#include "util.h"
|
|
#include "log_flags.h"
|
|
#include "filesys.h"
|
|
#include "network.h"
|
|
#include "client_msgs.h"
|
|
#include "../sched/edf_sim.h"
|
|
#include "sim.h"
|
|
|
|
#define SCHED_RETRY_DELAY_MIN 60 // 1 minute
|
|
#define SCHED_RETRY_DELAY_MAX (60*60*4) // 4 hours
|
|
|
|
#ifdef _WIN32
|
|
#define SIM_EXEC "..\\boincsim"
|
|
#else
|
|
#define SIM_EXEC "../sim"
|
|
#endif
|
|
|
|
CLIENT_STATE gstate;
|
|
COPROC_CUDA* coproc_cuda;
|
|
COPROC_ATI* coproc_ati;
|
|
NET_STATUS net_status;
|
|
bool user_active;
|
|
double duration = 86400, delta = 60;
|
|
FILE* logfile;
|
|
bool running;
|
|
double running_time = 0;
|
|
bool server_uses_workload = false;
|
|
bool dcf_dont_use;
|
|
bool dcf_stats;
|
|
bool dual_dcf;
|
|
bool cpu_sched_rr_only;
|
|
bool work_fetch_old;
|
|
int line_limit = 1000000;
|
|
|
|
SIM_RESULTS sim_results;
|
|
|
|
void SIM_PROJECT::update_dcf_stats(RESULT* rp) {
|
|
double raw_ratio = rp->final_cpu_time/rp->estimated_duration_uncorrected();
|
|
// see http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Algorithm_III
|
|
++completed_task_count;
|
|
double delta = raw_ratio - completions_ratio_mean;
|
|
completions_ratio_mean += delta / completed_task_count;
|
|
completions_ratio_s += delta * ( raw_ratio - completions_ratio_mean);
|
|
if (completed_task_count > 1) {
|
|
completions_ratio_stdev = sqrt(completions_ratio_s / (completed_task_count - 1));
|
|
double required_stdev = (raw_ratio - completions_ratio_mean) / completions_ratio_stdev;
|
|
if (required_stdev > completions_required_stdevs) {
|
|
completions_required_stdevs = std::min(required_stdev, 7.0);
|
|
}
|
|
}
|
|
duration_correction_factor = completions_ratio_mean +
|
|
completions_required_stdevs * completions_ratio_stdev;
|
|
return;
|
|
}
|
|
|
|
// generate a job; pick a random app,
|
|
// and pick a FLOP count from its distribution
|
|
//
|
|
void CLIENT_STATE::make_job(SIM_PROJECT* p, WORKUNIT* wup, RESULT* rp) {
|
|
SIM_APP* ap1, *ap=0;
|
|
double net_fpops = host_info.p_fpops;
|
|
double x = drand();
|
|
unsigned int i;
|
|
|
|
for (i=0; i<apps.size();i++) {
|
|
ap1 = (SIM_APP*)apps[i];
|
|
if (ap1->project != p) continue;
|
|
x -= ap1->weight;
|
|
if (x <= 0) {
|
|
ap = ap1;
|
|
break;
|
|
}
|
|
}
|
|
if (!ap) {
|
|
printf("ERROR-NO APP\n");
|
|
exit(1);
|
|
}
|
|
rp->clear();
|
|
rp->avp = 0;
|
|
for (i=0; i<gstate.app_versions.size(); i++) {
|
|
APP_VERSION* avp = gstate.app_versions[i];
|
|
if (avp->app == ap) {
|
|
rp->avp = avp;
|
|
break;
|
|
}
|
|
}
|
|
if (!rp->avp) {
|
|
printf("ERROR - NO APP VERSION\n");
|
|
exit(1);
|
|
}
|
|
rp->project = p;
|
|
rp->wup = wup;
|
|
sprintf(rp->name, "%s_%d", p->project_name, p->result_index++);
|
|
wup->project = p;
|
|
wup->rsc_fpops_est = ap->fpops_est;
|
|
double ops = ap->fpops.sample();
|
|
if (ops < 0) ops = 0;
|
|
rp->final_cpu_time = ops/net_fpops;
|
|
rp->report_deadline = now + ap->latency_bound;
|
|
}
|
|
|
|
// process ready-to-report results
|
|
//
|
|
void CLIENT_STATE::handle_completed_results() {
|
|
char buf[256];
|
|
vector<RESULT*>::iterator result_iter;
|
|
|
|
result_iter = results.begin();
|
|
while (result_iter != results.end()) {
|
|
RESULT* rp = *result_iter;
|
|
if (rp->ready_to_report) {
|
|
sprintf(buf, "result %s reported; %s<br>",
|
|
rp->name,
|
|
(gstate.now > rp->report_deadline)?
|
|
"<font color=#cc0000>MISSED DEADLINE</font>":
|
|
"<font color=#00cc00>MADE DEADLINE</font>"
|
|
);
|
|
SIM_PROJECT* spp = (SIM_PROJECT*)rp->project;
|
|
if (gstate.now > rp->report_deadline) {
|
|
sim_results.cpu_wasted += rp->final_cpu_time;
|
|
sim_results.nresults_missed_deadline++;
|
|
spp->project_results.nresults_missed_deadline++;
|
|
spp->project_results.cpu_wasted += rp->final_cpu_time;
|
|
} else {
|
|
sim_results.cpu_used += rp->final_cpu_time;
|
|
sim_results.nresults_met_deadline++;
|
|
spp->project_results.nresults_met_deadline++;
|
|
spp->project_results.cpu_used += rp->final_cpu_time;
|
|
}
|
|
gstate.html_msg += buf;
|
|
delete rp;
|
|
result_iter = results.erase(result_iter);
|
|
} else {
|
|
result_iter++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// convert results in progress to IP_RESULTs,
|
|
// and get an initial schedule for them
|
|
//
|
|
void CLIENT_STATE::get_workload(vector<IP_RESULT>& ip_results) {
|
|
for (unsigned int i=0; i<results.size(); i++) {
|
|
RESULT* rp = results[i];
|
|
double x = rp->estimated_time_remaining(false);
|
|
if (x == 0) continue;
|
|
IP_RESULT ipr(rp->name, rp->report_deadline, x);
|
|
ip_results.push_back(ipr);
|
|
}
|
|
init_ip_results(work_buf_min(), ncpus, ip_results);
|
|
}
|
|
|
|
// simulate trying to do an RPC
|
|
// return false if we didn't actually do one
|
|
//
|
|
bool CLIENT_STATE::simulate_rpc(PROJECT* _p) {
|
|
char buf[256];
|
|
SIM_PROJECT* p = (SIM_PROJECT*) _p;
|
|
static double last_time=-1e9;
|
|
vector<IP_RESULT> ip_results;
|
|
int infeasible_count = 0;
|
|
|
|
double diff = now - last_time;
|
|
if (diff && diff < host_info.connection_interval) {
|
|
msg_printf(NULL, MSG_INFO,
|
|
"simulate_rpc: too soon %f < %f",
|
|
diff, host_info.connection_interval
|
|
);
|
|
return false;
|
|
}
|
|
last_time = now;
|
|
|
|
sprintf(buf, "RPC to %s; asking for %f/%.2f<br>",
|
|
p->project_name, cpu_work_fetch.req_secs, cpu_work_fetch.req_instances
|
|
);
|
|
html_msg += buf;
|
|
|
|
msg_printf(0, MSG_INFO, buf);
|
|
|
|
handle_completed_results();
|
|
|
|
if (server_uses_workload) {
|
|
get_workload(ip_results);
|
|
}
|
|
|
|
bool sent_something = false;
|
|
double work_left = cpu_work_fetch.req_secs;
|
|
double instances_needed = cpu_work_fetch.req_instances;
|
|
while (work_left > 0 || instances_needed>0) {
|
|
RESULT* rp = new RESULT;
|
|
WORKUNIT* wup = new WORKUNIT;
|
|
make_job(p, wup, rp);
|
|
|
|
if (server_uses_workload) {
|
|
IP_RESULT c(rp->name, rp->report_deadline, rp->final_cpu_time);
|
|
if (check_candidate(c, ncpus, ip_results)) {
|
|
ip_results.push_back(c);
|
|
} else {
|
|
delete rp;
|
|
delete wup;
|
|
if (++infeasible_count > p->max_infeasible_count) {
|
|
p->min_rpc_time = now + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
sent_something = true;
|
|
rp->set_state(RESULT_FILES_DOWNLOADED, "simulate_rpc");
|
|
results.push_back(rp);
|
|
sprintf(buf, "got job %s: CPU time %.2f, deadline %s<br>",
|
|
rp->name, rp->final_cpu_time, time_to_string(rp->report_deadline)
|
|
);
|
|
html_msg += buf;
|
|
work_left -= p->duration_correction_factor*wup->rsc_fpops_est/host_info.p_fpops;
|
|
instances_needed -= 1;
|
|
}
|
|
|
|
if (cpu_work_fetch.req_secs > 0 && !sent_something) {
|
|
p->backoff();
|
|
}
|
|
p->nrpc_failures = 0;
|
|
if (sent_something) {
|
|
request_schedule_cpus("simulate_rpc");
|
|
request_work_fetch("simulate_rpc");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SIM_PROJECT::backoff() {
|
|
nrpc_failures++;
|
|
double backoff = calculate_exponential_backoff(
|
|
nrpc_failures, SCHED_RETRY_DELAY_MIN, SCHED_RETRY_DELAY_MAX
|
|
);
|
|
min_rpc_time = gstate.now + backoff;
|
|
}
|
|
|
|
bool CLIENT_STATE::scheduler_rpc_poll() {
|
|
PROJECT *p;
|
|
bool action = false;
|
|
static double last_time=0;
|
|
static double last_work_fetch_time = 0;
|
|
double elapsed_time;
|
|
|
|
// check only every 5 sec
|
|
//
|
|
if (now - last_time < SCHEDULER_RPC_POLL_PERIOD) {
|
|
msg_printf(NULL, MSG_INFO, "RPC poll: not time %f - %f < %f",
|
|
now, last_time, SCHEDULER_RPC_POLL_PERIOD
|
|
);
|
|
return false;
|
|
}
|
|
last_time = now;
|
|
|
|
msg_printf(NULL, MSG_INFO, "RPC poll start");
|
|
while (1) {
|
|
p = next_project_sched_rpc_pending();
|
|
if (p) {
|
|
work_fetch.compute_work_request(p);
|
|
action = simulate_rpc(p);
|
|
break;
|
|
}
|
|
|
|
p = find_project_with_overdue_results();
|
|
if (p) {
|
|
work_fetch.compute_work_request(p);
|
|
action = simulate_rpc(p);
|
|
break;
|
|
}
|
|
|
|
// should we check work fetch? Do this at most once/minute
|
|
|
|
if (must_check_work_fetch) {
|
|
last_work_fetch_time = 0;
|
|
}
|
|
elapsed_time = now - last_work_fetch_time;
|
|
if (elapsed_time < WORK_FETCH_PERIOD) {
|
|
return false;
|
|
}
|
|
must_check_work_fetch = false;
|
|
last_work_fetch_time = now;
|
|
|
|
p = work_fetch.choose_project();
|
|
if (p) {
|
|
action = simulate_rpc(p);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (action) {
|
|
msg_printf(p, MSG_INFO, "RPC poll: did an RPC");
|
|
} else {
|
|
msg_printf(0, MSG_INFO, "RPC poll: didn't do an RPC");
|
|
}
|
|
return action;
|
|
}
|
|
|
|
bool ACTIVE_TASK_SET::poll() {
|
|
unsigned int i;
|
|
char buf[256];
|
|
bool action = false;
|
|
static double last_time = 0;
|
|
double diff = gstate.now - last_time;
|
|
if (diff < 1.0) return false;
|
|
last_time = gstate.now;
|
|
SIM_PROJECT* p;
|
|
|
|
if (!running) return false;
|
|
|
|
for (i=0; i<gstate.projects.size(); i++) {
|
|
p = (SIM_PROJECT*) gstate.projects[i];
|
|
p->idle = true;
|
|
sprintf(buf, "%s STD: %f LTD %f<br>",
|
|
p->project_name, p->cpu_pwf.short_term_debt,
|
|
p->pwf.overall_debt
|
|
);
|
|
gstate.html_msg += buf;
|
|
}
|
|
|
|
int n=0;
|
|
for (i=0; i<active_tasks.size(); i++) {
|
|
ACTIVE_TASK* atp = active_tasks[i];
|
|
switch (atp->task_state()) {
|
|
case PROCESS_EXECUTING:
|
|
atp->cpu_time_left -= diff;
|
|
atp->current_cpu_time += diff;
|
|
RESULT* rp = atp->result;
|
|
|
|
double cpu_time_used = rp->final_cpu_time - atp->cpu_time_left;
|
|
atp->fraction_done = cpu_time_used/rp->final_cpu_time;
|
|
atp->checkpoint_wall_time = gstate.now;
|
|
|
|
if (atp->cpu_time_left <= 0) {
|
|
atp->set_task_state(PROCESS_EXITED, "poll");
|
|
rp->exit_status = 0;
|
|
rp->ready_to_report = true;
|
|
gstate.request_schedule_cpus("ATP poll");
|
|
gstate.request_work_fetch("ATP poll");
|
|
sprintf(buf, "result %s finished<br>", rp->name);
|
|
gstate.html_msg += buf;
|
|
action = true;
|
|
}
|
|
((SIM_PROJECT*)rp->project)->idle = false;
|
|
n++;
|
|
}
|
|
}
|
|
if (n < gstate.ncpus) {
|
|
sim_results.cpu_idle += diff*(gstate.ncpus-n);
|
|
}
|
|
if (n > gstate.ncpus) {
|
|
sprintf(buf, "TOO MANY JOBS RUNNING");
|
|
gstate.html_msg += buf;
|
|
}
|
|
|
|
for (i=0; i<gstate.projects.size(); i++) {
|
|
p = (SIM_PROJECT*) gstate.projects[i];
|
|
if (p->idle) {
|
|
p->idle_time += diff;
|
|
p->idle_time_sumsq += diff*(p->idle_time*p->idle_time);
|
|
} else {
|
|
p->idle_time = 0;
|
|
}
|
|
}
|
|
running_time += diff;
|
|
|
|
return action;
|
|
}
|
|
|
|
int SIM_APP::parse(XML_PARSER& xp) {
|
|
char tag[256];
|
|
bool is_tag;
|
|
int retval;
|
|
|
|
weight = 1;
|
|
while(!xp.get(tag, sizeof(tag), is_tag)) {
|
|
if (!is_tag) return ERR_XML_PARSE;
|
|
if (!strcmp(tag, "/app")) {
|
|
return 0;
|
|
}
|
|
else if (xp.parse_double(tag, "latency_bound", latency_bound)) continue;
|
|
else if (xp.parse_double(tag, "fpops_est", fpops_est)) continue;
|
|
else if (xp.parse_double(tag, "weight", weight)) continue;
|
|
else if (!strcmp(tag, "fpops")) {
|
|
retval = fpops.parse(xp, "/fpops");
|
|
if (retval) return retval;
|
|
} else if (!strcmp(tag, "checkpoint_period")) {
|
|
retval = checkpoint_period.parse(xp, "/checkpoint_period");
|
|
if (retval) return retval;
|
|
} else if (xp.parse_double(tag, "working_set", working_set)) continue;
|
|
else {
|
|
printf("unrecognized: %s\n", tag);
|
|
return ERR_XML_PARSE;
|
|
}
|
|
}
|
|
return ERR_XML_PARSE;
|
|
}
|
|
|
|
// return the fraction of CPU time that was spent in violation of shares
|
|
// i.e., if a project got X and it should have got Y,
|
|
// add up |X-Y| over all projects, and divide by total CPU
|
|
//
|
|
double CLIENT_STATE::share_violation() {
|
|
unsigned int i;
|
|
|
|
double tot = 0, trs=0;
|
|
for (i=0; i<projects.size(); i++) {
|
|
SIM_PROJECT* p = (SIM_PROJECT*) projects[i];
|
|
tot += p->project_results.cpu_used + p->project_results.cpu_wasted;
|
|
trs += p->resource_share;
|
|
}
|
|
double sum = 0;
|
|
for (i=0; i<projects.size(); i++) {
|
|
SIM_PROJECT* p = (SIM_PROJECT*) projects[i];
|
|
double t = p->project_results.cpu_used + p->project_results.cpu_wasted;
|
|
double rs = p->resource_share/trs;
|
|
double rt = tot*rs;
|
|
sum += fabs(t - rt);
|
|
}
|
|
return sum/tot;
|
|
|
|
}
|
|
|
|
// "monotony" is defined as follows:
|
|
// for each project P, maintain R(P), the time since P last ran,
|
|
// let S(P) be the RMS of R(P).
|
|
// Let X = mean(S(P))/(sched_interval*nprojects)
|
|
// (the *nprojects reflects the fact that in the limit of nprojects,
|
|
// each one waits for a time to run proportional to nprojects)
|
|
// X varies from zero (no monotony) to infinity.
|
|
// X is one in the case of round-robin on 1 CPU.
|
|
// Let monotony = 1-1/(x+1)
|
|
//
|
|
double CLIENT_STATE::monotony() {
|
|
double sum = 0;
|
|
double schedint = global_prefs.cpu_scheduling_period();
|
|
unsigned int i;
|
|
for (i=0; i<projects.size(); i++) {
|
|
SIM_PROJECT* p = (SIM_PROJECT*) projects[i];
|
|
double avg_ss = p->idle_time_sumsq/running_time;
|
|
double s = sqrt(avg_ss);
|
|
sum += s;
|
|
}
|
|
int n = (int)projects.size();
|
|
double x = sum/(n*schedint*n);
|
|
double m = 1-(1/(x+1));
|
|
//printf("sum: %f; x: %f m: %f\n", sum, x, m);
|
|
return m;
|
|
}
|
|
|
|
// the CPU totals are there; compute the other fields
|
|
//
|
|
void SIM_RESULTS::compute() {
|
|
double total = cpu_used + cpu_wasted + cpu_idle;
|
|
cpu_wasted_frac = cpu_wasted/total;
|
|
cpu_idle_frac = cpu_idle/total;
|
|
share_violation = gstate.share_violation();
|
|
monotony = gstate.monotony();
|
|
}
|
|
|
|
// top-level results (for aggregating multiple simulations)
|
|
//
|
|
void SIM_RESULTS::print(FILE* f, const char* title) {
|
|
if (title) {
|
|
fprintf(f, "%s: ", title);
|
|
}
|
|
fprintf(f, "wasted_frac %f idle_frac %f share_violation %f monotony %f\n",
|
|
cpu_wasted_frac, cpu_idle_frac, share_violation, monotony
|
|
);
|
|
}
|
|
|
|
void SIM_RESULTS::parse(FILE* f) {
|
|
fscanf(f, "wasted_frac %lf idle_frac %lf share_violation %lf monotony %lf",
|
|
&cpu_wasted_frac, &cpu_idle_frac, &share_violation, &monotony
|
|
);
|
|
}
|
|
|
|
void SIM_RESULTS::add(SIM_RESULTS& r) {
|
|
cpu_wasted_frac += r.cpu_wasted_frac;
|
|
cpu_idle_frac += r.cpu_idle_frac;
|
|
share_violation += r.share_violation;
|
|
monotony += r.monotony;
|
|
}
|
|
|
|
void SIM_RESULTS::divide(int n) {
|
|
cpu_wasted_frac /= n;
|
|
cpu_idle_frac /= n;
|
|
share_violation /= n;
|
|
monotony /= n;
|
|
}
|
|
|
|
void SIM_RESULTS::clear() {
|
|
memset(this, 0, sizeof(*this));
|
|
}
|
|
|
|
void SIM_PROJECT::print_results(FILE* f, SIM_RESULTS& sr) {
|
|
double t = project_results.cpu_used + project_results.cpu_wasted;
|
|
double gt = sr.cpu_used + sr.cpu_wasted;
|
|
fprintf(f, "%s: share %.2f total CPU %2f (%.2f%%)\n"
|
|
" used %.2f wasted %.2f\n"
|
|
" met %d missed %d\n",
|
|
project_name, resource_share,
|
|
t, (t/gt)*100,
|
|
project_results.cpu_used,
|
|
project_results.cpu_wasted,
|
|
project_results.nresults_met_deadline,
|
|
project_results.nresults_missed_deadline
|
|
);
|
|
}
|
|
|
|
char* colors[] = {
|
|
"#ffffdd",
|
|
"#ffddff",
|
|
"#ddffff",
|
|
"#ddffdd",
|
|
"#ddddff",
|
|
"#ffdddd",
|
|
};
|
|
|
|
static int outfile_num=0;
|
|
|
|
void CLIENT_STATE::html_start(bool show_prev) {
|
|
char buf[256];
|
|
|
|
sprintf(buf, "sim_out_%d.html", outfile_num++);
|
|
html_out = fopen(buf, "w");
|
|
if (!html_out) {
|
|
fprintf(stderr, "can't open %s for writing\n", buf);
|
|
exit(1);
|
|
}
|
|
setbuf(html_out, 0);
|
|
fprintf(html_out, "<h2>Simulator output</h2>\n");
|
|
if (show_prev) {
|
|
fprintf(html_out,
|
|
"<a href=sim_out_%d.html>Previous file</a><p>\n",
|
|
outfile_num-2
|
|
);
|
|
}
|
|
fprintf(html_out,
|
|
"<a href=sim_log.txt>message log</a><p>"
|
|
"<table border=1><tr><th>Time</th>\n"
|
|
);
|
|
for (int i=0; i<ncpus; i++) {
|
|
fprintf(html_out,
|
|
"<th>CPU %d<br><font size=-2>Job name and estimated time left<br>color denotes project<br>* means EDF mode</font></th>", i
|
|
);
|
|
}
|
|
fprintf(html_out, "<th>Notes</th></tr>\n");
|
|
}
|
|
|
|
void CLIENT_STATE::html_rec() {
|
|
static int line_num=0;
|
|
|
|
fprintf(html_out, "<tr><td>%s</td>", time_to_string(now));
|
|
|
|
if (!running) {
|
|
for (int j=0; j<ncpus; j++) {
|
|
fprintf(html_out, "<td bgcolor=#aaaaaa>OFF</td>");
|
|
}
|
|
} else {
|
|
int n=0;
|
|
for (unsigned int i=0; i<active_tasks.active_tasks.size(); i++) {
|
|
ACTIVE_TASK* atp = active_tasks.active_tasks[i];
|
|
if (atp->task_state() == PROCESS_EXECUTING) {
|
|
SIM_PROJECT* p = (SIM_PROJECT*)atp->result->project;
|
|
fprintf(html_out, "<td bgcolor=%s>%s%s: %.2f</td>",
|
|
colors[p->index],
|
|
atp->result->rr_sim_misses_deadline?"*":"",
|
|
atp->result->name, atp->cpu_time_left
|
|
);
|
|
n++;
|
|
}
|
|
}
|
|
while (n<ncpus) {
|
|
fprintf(html_out, "<td>IDLE</td>");
|
|
n++;
|
|
}
|
|
}
|
|
fprintf(html_out, "<td><font size=-2>%s</font></td></tr>\n", html_msg.c_str());
|
|
html_msg = "";
|
|
|
|
if (++line_num == line_limit) {
|
|
line_num = 0;
|
|
html_end(true);
|
|
html_start(true);
|
|
}
|
|
}
|
|
|
|
void CLIENT_STATE::html_end(bool show_next) {
|
|
fprintf(html_out, "</table>");
|
|
if (show_next) {
|
|
fprintf(html_out,
|
|
"<p><a href=sim_out_%d.html>Next file</a>\n",
|
|
outfile_num
|
|
);
|
|
} else {
|
|
fprintf(html_out, "<pre>\n");
|
|
sim_results.compute();
|
|
sim_results.print(html_out);
|
|
print_project_results(html_out);
|
|
fprintf(html_out, "</pre>\n");
|
|
}
|
|
if (show_next) {
|
|
fprintf(html_out, "<p><a href=sim_out_last.html>Last file</a>\n");
|
|
} else {
|
|
char buf[256];
|
|
sprintf(buf, "sim_out_%d.html", outfile_num-1);
|
|
#ifndef _WIN32
|
|
symlink(buf, "sim_out_last.html");
|
|
#endif
|
|
}
|
|
fclose(html_out);
|
|
}
|
|
|
|
void CLIENT_STATE::simulate() {
|
|
bool action;
|
|
double start = START_TIME;
|
|
now = start;
|
|
html_start(false);
|
|
msg_printf(0, MSG_INFO,
|
|
"starting simultion. delta %f duration %f", delta, duration
|
|
);
|
|
while (1) {
|
|
running = host_info.available.sample(now);
|
|
while (1) {
|
|
msg_printf(0, MSG_INFO, "polling");
|
|
action = active_tasks.poll();
|
|
if (running) {
|
|
action |= handle_finished_apps();
|
|
action |= possibly_schedule_cpus();
|
|
action |= enforce_schedule();
|
|
action |= scheduler_rpc_poll();
|
|
}
|
|
msg_printf(0, MSG_INFO, action?"did action":"did no action");
|
|
if (!action) break;
|
|
}
|
|
now += delta;
|
|
msg_printf(0, MSG_INFO, "took time step");
|
|
for (unsigned int i=0; i<active_tasks.active_tasks.size(); i++) {
|
|
ACTIVE_TASK* atp = active_tasks.active_tasks[i];
|
|
if (atp->task_state() == PROCESS_EXECUTING) {
|
|
atp->elapsed_time += delta;
|
|
}
|
|
}
|
|
html_rec();
|
|
if (now > start + duration) break;
|
|
}
|
|
html_end(false);
|
|
}
|
|
|
|
void parse_error(char* file, int retval) {
|
|
printf("can't parse %s: %d\n", file, retval);
|
|
exit(1);
|
|
}
|
|
|
|
void help(char* prog) {
|
|
fprintf(stderr, "usage: %s\n"
|
|
"[--duration X]\n"
|
|
"[--delta X]\n"
|
|
"[--server_uses_workload]\n"
|
|
"[--dcf_dont_user]\n"
|
|
"[--dcf_stats]\n"
|
|
"[--dual_dcf]\n"
|
|
"[--cpu_sched_rr_only]\n"
|
|
"[--work_fetch_old]\n"
|
|
"[--dirs ...]\n",
|
|
prog
|
|
);
|
|
exit(1);
|
|
}
|
|
|
|
char* next_arg(int argc, char** argv, int& i) {
|
|
if (i >= argc) {
|
|
fprintf(stderr, "Missing command-line argument\n");
|
|
help(argv[0]);
|
|
}
|
|
return argv[i++];
|
|
}
|
|
|
|
#define PROJECTS_FILE "sim_projects.xml"
|
|
#define HOST_FILE "sim_host.xml"
|
|
#define PREFS_FILE "sim_prefs.xml"
|
|
#define SUMMARY_FILE "sim_summary.txt"
|
|
#define LOG_FILE "sim_log.txt"
|
|
|
|
int main(int argc, char** argv) {
|
|
int i, retval;
|
|
vector<std::string> dirs;
|
|
|
|
logfile = fopen("sim_log.txt", "w");
|
|
if (!logfile) {
|
|
fprintf(stderr, "Can't open sim_log.txt\n");
|
|
exit(1);
|
|
}
|
|
|
|
sim_results.clear();
|
|
for (i=1; i<argc;) {
|
|
char* opt = argv[i++];
|
|
if (!strcmp(opt, "--duration")) {
|
|
duration = atof(next_arg(argc, argv, i));
|
|
} else if (!strcmp(opt, "--delta")) {
|
|
delta = atof(next_arg(argc, argv, i));
|
|
} else if (!strcmp(opt, "--dirs")) {
|
|
while (i<argc) {
|
|
dirs.push_back(argv[i++]);
|
|
}
|
|
} else if (!strcmp(opt, "--server_uses_workload")) {
|
|
server_uses_workload = true;
|
|
} else if (!strcmp(opt, "--dcf_dont_use")) {
|
|
dcf_dont_use = true;
|
|
} else if (!strcmp(opt, "--dcf_stats")) {
|
|
dcf_stats = true;
|
|
} else if (!strcmp(opt, "--dual_dcf")) {
|
|
dual_dcf = true;
|
|
dcf_stats = true;
|
|
} else if (!strcmp(opt, "--cpu_sched_rr_only")) {
|
|
cpu_sched_rr_only = true;
|
|
} else if (!strcmp(opt, "--work_fetch_old")) {
|
|
work_fetch_old = true;
|
|
} else if (!strcmp(opt, "--line_limit")) {
|
|
line_limit = atoi(next_arg(argc, argv, i));
|
|
} else {
|
|
help(argv[0]);
|
|
}
|
|
}
|
|
|
|
if (duration <= 0) {
|
|
printf("non-pos duration\n");
|
|
exit(1);
|
|
}
|
|
if (delta <= 0) {
|
|
printf("non-pos delta\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (dirs.size()) {
|
|
// If we need to do several simulations,
|
|
// use system() to do each one in a separate process,
|
|
// because there are lots of static variables and we need to ensure
|
|
// that they start off with the right initial values
|
|
//
|
|
unsigned int i;
|
|
SIM_RESULTS total_results;
|
|
total_results.clear();
|
|
for (i=0; i<dirs.size(); i++) {
|
|
std::string dir = dirs[i];
|
|
retval = chdir(dir.c_str());
|
|
if (retval) {
|
|
fprintf(stderr, "can't chdir into %s: ", dir.c_str());
|
|
perror("chdir");
|
|
continue;
|
|
}
|
|
char buf[256];
|
|
sprintf(
|
|
buf, "%s --duration %f --delta %f > %s",
|
|
SIM_EXEC, duration, delta, SUMMARY_FILE
|
|
);
|
|
retval = system(buf);
|
|
if (retval) {
|
|
printf("simulation in %s failed\n", dir.c_str());
|
|
exit(1);
|
|
}
|
|
FILE* f = fopen(SUMMARY_FILE, "r");
|
|
sim_results.parse(f);
|
|
fclose(f);
|
|
sim_results.print(stdout, dir.c_str());
|
|
total_results.add(sim_results);
|
|
chdir("..");
|
|
}
|
|
total_results.divide((int)(dirs.size()));
|
|
total_results.print(stdout, "Total");
|
|
} else {
|
|
msg_printf(0, MSG_INFO, "SIMULATION START");
|
|
read_config_file(true);
|
|
config.show();
|
|
|
|
int retval;
|
|
bool flag;
|
|
|
|
retval = gstate.parse_host(HOST_FILE);
|
|
if (retval) parse_error(HOST_FILE, retval);
|
|
retval = gstate.parse_projects(PROJECTS_FILE);
|
|
if (retval) parse_error(PROJECTS_FILE, retval);
|
|
retval = gstate.global_prefs.parse_file(PREFS_FILE, "", flag);
|
|
if (retval) parse_error(PREFS_FILE, retval);
|
|
|
|
gstate.set_ncpus();
|
|
work_fetch.init();
|
|
gstate.request_work_fetch("init");
|
|
gstate.simulate();
|
|
|
|
sim_results.compute();
|
|
|
|
// print machine-readable first
|
|
sim_results.print(stdout);
|
|
|
|
// then other
|
|
gstate.print_project_results(stdout);
|
|
}
|
|
}
|