163 inv.nH = m.
procs.size();
164 inv.nT = m.
tasks.size();
166 inv.nA = m.
acts.size();
167 for (
const auto& p : m.
procs) inv.hName.push_back(p.name);
168 for (
const auto& t : m.
tasks) inv.tName.push_back(t.name);
169 for (
const auto& e : m.
entries) inv.eName.push_back(e.name);
170 for (
const auto& a : m.
acts) inv.aName.push_back(a.name);
171 for (std::size_t h = 0; h < inv.nH; ++h) {
174 fail(
"Element \"" + inv.hName[h] +
"\" uses admission constraints, load-dependent scaling or "
175 "server pools, which lqn_focus does not support.");
177 inv.hMult.resize(inv.nH);
178 inv.hSpeed.resize(inv.nH);
179 inv.forcedH.assign(inv.nH,
false);
180 for (std::size_t h = 0; h < inv.nH; ++h) {
181 const auto& p = m.
procs[h];
182 inv.hMult[h] = p.mult;
183 inv.hSpeed[h] = p.speed_factor;
184 inv.forcedH[h] = p.repl > 1 ||
is_prio(p.sched);
186 inv.tMult.resize(inv.nT);
187 inv.tHost.resize(inv.nT);
188 inv.tIsRef.assign(inv.nT,
false);
189 inv.forcedT.assign(inv.nT,
false);
190 for (std::size_t t = 0; t < inv.nT; ++t) {
191 const auto& tk = m.
tasks[t];
192 if (tk.nitems > 0)
fail(
"Task \"" + tk.name +
"\" is a cache task, which lqn_focus does not support.");
193 if (!tk.linconrows.empty() || tk.lincon_A.rows() > 0 || !tk.lldscaling.empty() ||
194 static_cast<bool>(tk.cdscaling) ||
static_cast<bool>(tk.jdscaling) || !tk.pools.empty())
195 fail(
"Element \"" + tk.name +
"\" uses admission constraints, load-dependent scaling or "
196 "server pools, which lqn_focus does not support.");
197 if (tk.proc_slot >= inv.nH)
fail(
"Task \"" + tk.name +
"\" has no host.");
198 inv.tMult[t] = tk.mult;
199 inv.tHost[t] = tk.proc_slot;
200 inv.tIsRef[t] = tk.sched == SchedStrategy::REF;
201 bool f = inv.tIsRef[t] || tk.repl > 1 || tk.priority != 0 ||
is_prio(tk.sched) ||
202 has_time(tk.setuptime) ||
has_time(tk.delayofftime) || !tk.fanin.empty() || !tk.fanout.empty();
203 for (
const auto& pr : tk.precedences)
204 if (pr.pretype == PrecedenceType::PRE_AND || pr.posttype == PrecedenceType::POST_AND) f =
true;
207 inv.eTask.resize(inv.nE);
208 for (std::size_t e = 0; e < inv.nE; ++e) {
210 if (en.cardinality > 0)
fail(
"Entry \"" + en.name +
"\" is not a plain entry, which lqn_focus does not support.");
211 inv.eTask[e] = en.task_slot;
213 auto dest = [&](
const std::string& n) {
214 std::size_t e2 =
index_of(inv.eName, n);
215 if (e2 == inv.nE)
fail(
"Call destination \"" + n +
"\" is not an entry of the model.");
218 inv.aTask.resize(inv.nA);
219 inv.aDem.resize(inv.nA);
220 for (std::size_t a = 0; a < inv.nA; ++a) {
221 const auto& ac = m.
acts[a];
222 const std::size_t t = ac.task_slot;
224 inv.aDem[a] =
dmean(ac.hostdem);
225 if (!ac.call_groups.empty())
226 fail(
"Activity \"" + ac.name +
"\" uses call groups, which lqn_focus does not support.");
227 if (ac.phase > 1 ||
has_time(ac.thinktime)) inv.forcedT[t] =
true;
228 for (
const auto& c : ac.sync_calls) inv.sync.push_back({a, dest(c.dest), c.mean});
229 for (
const auto& c : ac.async_calls) {
230 const std::size_t e2 = dest(c.dest);
231 inv.async.push_back({a, e2, c.mean});
232 inv.forcedT[t] =
true;
233 inv.forcedT[inv.eTask[e2]] =
true;
236 for (std::size_t e = 0; e < inv.nE; ++e) {
238 if (en.has_arrival) inv.forcedT[inv.eTask[e]] =
true;
239 for (std::size_t k = 0; k < en.fwd_dest.size(); ++k) {
240 const std::size_t e2 = dest(en.fwd_dest[k]);
241 inv.fwd.push_back({e, e2, en.fwd_prob[k]});
242 inv.forcedT[inv.eTask[e]] =
true;
243 inv.forcedT[inv.eTask[e2]] =
true;
247 R.assign(inv.nT, std::vector<bool>(inv.nT,
false));
248 for (
const auto& c : inv.sync) R[inv.aTask[c.src]][inv.eTask[c.dst]] =
true;
249 for (
const auto& c : inv.async) R[inv.aTask[c.src]][inv.eTask[c.dst]] =
true;
250 for (
const auto& c : inv.fwd) R[inv.eTask[c.src]][inv.eTask[c.dst]] =
true;
251 for (std::size_t k = 0; k < inv.nT; ++k)
252 for (std::size_t i = 0; i < inv.nT; ++i)
254 for (std::size_t j = 0; j < inv.nT; ++j)
255 if (R[k][j]) R[i][j] =
true;
256 for (std::size_t t = 0; t < inv.nT; ++t)
257 if (R[t][t])
fail(
"The task call graph has a cycle, which FSPT does not support.");
324 std::vector<bool> PT, std::vector<bool> PH,
const LqnFocusOptions&
opt,
bool validate) {
325 const double tol = GlobalConstants::CoarseTol;
326 const std::size_t nH = inv.nH, nT = inv.nT, nE = inv.nE, nA = inv.nA;
327 std::vector<int> gid;
328 std::vector<std::size_t> pidx;
329 std::vector<std::vector<double>> shadow;
334 for (std::size_t t = 0; t < nT; ++t)
335 if (PT[t]) pidx.push_back(t);
337 std::vector<std::vector<bool>> keys;
338 for (std::size_t t = 0; t < nT; ++t) {
340 std::vector<bool> key;
342 for (std::size_t p : pidx) {
343 key.push_back(inv.reach[p][t]);
344 any = any || inv.reach[p][t];
350 auto it = std::find(keys.begin(), keys.end(), key);
351 if (it == keys.end()) {
353 gid[t] =
static_cast<int>(keys.size());
355 gid[t] =
static_cast<int>(it - keys.begin()) + 1;
359 shadow.assign(ng + 1, std::vector<double>(nH, 1.0));
360 bool changed =
false;
361 for (std::size_t h = 0; h < nH && !changed; ++h) {
364 for (std::size_t t = 0; t < nT; ++t)
365 if (inv.tHost[t] == h && gid[t] > 0) gs.insert(gid[t]);
366 if (gs.size() < 2)
continue;
369 for (std::size_t t = 0; t < nT; ++t)
370 if (inv.tHost[t] == h && gid[t] > 0 && gid[t] != g) frac -= sat.
SHT[h][t];
382 std::set<std::string> all;
383 for (
const auto* v : {&inv.hName, &inv.tName, &inv.eName, &inv.aName}) all.insert(v->begin(), v->end());
384 std::vector<LqnFocusGroup> grp(ng);
385 for (std::size_t g = 1; g <= ng; ++g) {
387 std::set<std::size_t> hg;
388 std::size_t first = nT;
389 for (std::size_t t = 0; t < nT; ++t) {
390 if (gid[t] !=
static_cast<int>(g))
continue;
391 if (first == nT) first = t;
392 d.
tasks.push_back(inv.tName[t]);
393 d.
mult += inv.tMult[t];
394 hg.insert(inv.tHost[t]);
396 for (std::size_t p : pidx)
397 if (inv.reach[p][first]) d.
pi.push_back(inv.tName[p]);
398 const std::string k = std::to_string(g);
403 for (std::size_t h : hg) d.
hostMult += inv.hMult[h];
406 for (std::size_t h : hg) s += inv.hSpeed[h] * shadow[g][h] * inv.hMult[h];
410 std::vector<std::string> fmEntry(nE);
411 for (std::size_t e = 0; e < nE; ++e) {
412 const int g = gid[inv.eTask[e]];
413 fmEntry[e] = g == 0 ? inv.eName[e] : (g > 0 ? grp[g - 1].aggEntry : std::string());
415 for (
const auto& c : inv.sync) {
416 const int gs = gid[inv.aTask[c.src]], gd = gid[inv.eTask[c.dst]];
417 if (gd > 0 && gs != gd && gs >= 0) grp[gd - 1].rate += om.
aX[c.src] * c.mean;
419 for (std::size_t g = 1; g <= ng; ++g) {
421 const double rateA = std::max(d.
rate, GlobalConstants::Zero);
422 double work = 0, time = 0;
423 for (std::size_t a = 0; a < nA; ++a) {
424 const std::size_t t = inv.aTask[a];
425 if (gid[t] !=
static_cast<int>(g))
continue;
426 const std::size_t h = inv.tHost[t];
427 const double w = om.
aX[a] * inv.aDem[a];
429 time += w / (inv.hSpeed[h] * shadow[g][h]);
432 for (
const auto& c : inv.sync) {
433 if (gid[inv.aTask[c.src]] ==
static_cast<int>(g) && gid[inv.eTask[c.dst]] !=
static_cast<int>(g)) {
434 const std::string& dn = fmEntry[c.dst];
448 std::vector<std::size_t> hslot(nH, nH), tslot(nT, nT);
449 for (std::size_t h = 0; h < nH; ++h) {
450 if (!PH[h])
continue;
451 hslot[h] = fm.
procs.size();
454 for (std::size_t t = 0; t < nT; ++t) {
455 if (!PT[t])
continue;
456 tslot[t] = fm.
tasks.size();
457 auto tk = model.
tasks[t];
458 tk.proc_slot = hslot[inv.tHost[t]];
459 fm.
tasks.push_back(tk);
461 std::vector<std::size_t> aggTslot(ng);
462 for (std::size_t g = 0; g < ng; ++g) {
467 p.sched = SchedStrategy::PS;
470 p.sched = SchedStrategy::INF;
471 p.mult = std::numeric_limits<double>::infinity();
473 fm.
procs.push_back(p);
474 detail::RawTask<double> tk;
476 if (std::isfinite(d.
mult)) {
477 tk.sched = SchedStrategy::FCFS;
480 tk.sched = SchedStrategy::INF;
481 tk.mult = std::numeric_limits<double>::infinity();
483 tk.proc_slot = fm.
procs.size() - 1;
484 aggTslot[g] = fm.
tasks.size();
485 fm.
tasks.push_back(tk);
487 for (std::size_t e = 0; e < nE; ++e) {
488 if (!PT[inv.eTask[e]])
continue;
490 en.task_slot = tslot[inv.eTask[e]];
493 for (std::size_t g = 0; g < ng; ++g) {
494 detail::RawEntry<double> en;
495 en.name = grp[g].aggEntry;
496 en.task_slot = aggTslot[g];
497 en.reply_activities.push_back(grp[g].aggActivity);
500 for (std::size_t a = 0; a < nA; ++a) {
501 if (!PT[inv.aTask[a]])
continue;
502 auto ac = model.
acts[a];
503 ac.task_slot = tslot[inv.aTask[a]];
504 ac.sync_calls.clear();
505 for (
const auto& c : model.
acts[a].sync_calls) {
506 const std::string& dn = fmEntry[
index_of(inv.eName, c.dest)];
507 auto it = std::find_if(ac.sync_calls.begin(), ac.sync_calls.end(),
508 [&](
const detail::RawCall<double>& x) { return x.dest == dn; });
509 if (it == ac.sync_calls.end()) ac.sync_calls.push_back({dn, c.mean});
510 else it->mean += c.mean;
512 fm.
acts.push_back(ac);
514 for (std::size_t g = 0; g < ng; ++g) {
516 detail::RawActivity<double> ac;
521 ac.task_slot = aggTslot[g];
522 for (std::size_t k = 0; k < d.
callDests.size(); ++k) ac.sync_calls.push_back({d.callDests[k], d.callMeans[k]});
523 fm.
acts.push_back(ac);
527 std::size_t nPT = 0, nPH = 0;
528 for (std::size_t t = 0; t < nT; ++t) {
533 if (inv.forcedT[t]) res.
forcedTasks.push_back(inv.tName[t]);
534 if (gid[t] == -1) res.
droppedTasks.push_back(inv.tName[t]);
536 for (std::size_t h = 0; h < nH; ++h)
547 for (std::size_t t = 0; t < nT; ++t) res.
taskMap.push_back({inv.tName[t], map_name(gid[t], inv.tName[t], grp, true)});
548 for (std::size_t e = 0; e < nE; ++e) res.
entryMap.push_back({inv.eName[e], fmEntry[e]});
549 for (std::size_t a = 0; a < nA; ++a)
550 res.
activityMap.push_back({inv.aName[a], map_name(gid[inv.aTask[a]], inv.aName[a], grp, false)});
551 for (std::size_t h = 0; h < nH; ++h) {
553 res.
hostMap.push_back({inv.hName[h], inv.hName[h]});
557 for (std::size_t t = 0; t < nT; ++t)
558 if (inv.tHost[t] == h && gid[t] > 0) gs.insert(gid[t]);
559 if (gs.empty()) res.
hostMap.push_back({inv.hName[h],
""});
560 for (
int g : gs) res.
hostMap.push_back({inv.hName[h], grp[g - 1].aggHost});
562 double soutMax = 0, sallMax = 0;
564 for (std::size_t t = 0; t < nT; ++t) {
565 sallMax = std::max(sallMax, sat.
ST[t]);
567 soutMax = std::max(soutMax, sat.
ST[t]);
571 for (std::size_t h = 0; h < nH; ++h) {
572 sallMax = std::max(sallMax, sat.
SH[h]);
574 soutMax = std::max(soutMax, sat.
SH[h]);
578 res.
SR = (!anyOut || soutMax <= 0) ? std::numeric_limits<double>::infinity() : sallMax / soutMax;
580 res.trustedRange[0] = 1.0 / (res.SR - 2);
581 res.trustedRange[1] = res.SR - 2;
583 res.
FT =
static_cast<double>(nPT) / nT;
584 res.
F =
static_cast<double>(nPT + nPH + 2 * ng) / (nT + nH);
585 std::vector<std::size_t> refs;
586 for (std::size_t t = 0; t < nT; ++t)
589 res.refTasks.push_back(inv.tName[t]);
596 auto find = [&](
const std::string& n, std::size_t lo, std::size_t hi) {
597 for (std::size_t i = lo + 1; i <= hi; ++i)
598 if (l.
names[i] == n)
return i;
599 fail(
"element " + n +
" not found in the focused model.");
602 auto add = [&](
const std::string& n,
const char* ty,
double xo,
double xf,
double ro,
double rf,
double qo,
603 double qf,
double uo,
double uf) {
604 ret.
names.push_back(n);
605 ret.
types.push_back(ty);
606 ret.
X_om.push_back(xo);
607 ret.
X_fm.push_back(xf);
608 ret.
R_om.push_back(ro);
609 ret.
R_fm.push_back(rf);
610 ret.
Q_om.push_back(qo);
611 ret.
Q_fm.push_back(qf);
612 ret.
U_om.push_back(uo);
613 ret.
U_fm.push_back(uf);
615 std::map<std::size_t, std::size_t> taskRow;
616 for (std::size_t t = 0; t < nT; ++t) {
617 if (!PT[t])
continue;
618 const std::size_t i = find(inv.tName[t], l.
tshift, l.
eshift);
619 taskRow[t] = ret.
names.size();
620 add(inv.tName[t],
"task", om.
tX[t], s.
TN[i],
nan_(),
nan_(), om.
tQ[t], s.
QN[i], om.
tU[t], s.
UN[i]);
622 for (std::size_t e = 0; e < nE; ++e) {
623 if (!PT[inv.eTask[e]])
continue;
624 const std::size_t i = find(inv.eName[e], l.
eshift, l.
ashift);
625 add(inv.eName[e],
"entry", om.
eX[e], s.
TN[i], om.
eR[e], s.
RN[i], om.
eQ[e], s.
QN[i], om.
eU[e], s.
UN[i]);
627 for (std::size_t h = 0; h < nH; ++h) {
628 if (!PH[h])
continue;
629 const std::size_t i = find(inv.hName[h], 0, l.
nhosts);
632 std::size_t kmax = refs.size(), kmaxR = refs.size();
633 for (std::size_t k = 0; k < refs.size(); ++k) {
634 const std::size_t t = refs[k], row = taskRow[t];
635 const double xo = ret.
X_om[row], xf = ret.
X_fm[row];
637 if (zt <= GlobalConstants::FineTol) zt = 0;
638 res.
errRef[k] = (xf - xo) / xo;
639 const double ro = inv.tMult[t] / xo - zt, rf = inv.tMult[t] / xf - zt;
641 if (kmax == refs.size() || std::fabs(res.
errRef[k]) > std::fabs(res.
errRef[kmax])) kmax = k;
642 if (kmaxR == refs.size() || std::fabs(res.
errRespRef[k]) > std::fabs(res.
errRespRef[kmaxR])) kmaxR = k;
644 if (kmax < refs.size()) {
649 res.
fm = std::move(fm);
650 res.
groups = std::move(grp);