5#ifndef LINE_LANG_LQN_LQN_READER_H
6#define LINE_LANG_LQN_LQN_READER_H
51#include <unordered_map>
78 std::string bound_to_entry;
81 std::string call_order =
"STOCHASTIC";
82 std::size_t task_slot = 0;
83 std::vector<RawCall<T>> sync_calls;
84 std::vector<RawCall<T>> async_calls;
91 std::vector<std::pair<lang::RoutingStrategy, std::vector<std::string>>> call_groups;
98 std::vector<std::string> preacts;
99 std::vector<std::string> postacts;
100 std::vector<T> preparams;
101 std::vector<T> postparams;
102 bool has_quorum =
false;
103 std::size_t quorum = 0;
109 std::size_t task_slot = 0;
110 std::vector<std::string> reply_activities;
111 bool has_arrival =
false;
113 std::vector<std::string> fwd_dest;
114 std::vector<T> fwd_prob;
116 std::size_t cardinality = 0;
117 std::vector<T> popularity;
119 std::string type =
"PH1PH2";
131 std::vector<std::string> names;
132 std::vector<T> coeffs;
144struct RawServerPool {
148 std::vector<std::string> compatible;
159 Distrib<T> thinktime;
160 std::size_t proc_slot = 0;
162 std::vector<std::pair<std::string, double>> fanout;
163 std::vector<std::pair<std::string, double>> fanin;
164 std::vector<RawPrecedence<T>> precedences;
165 std::vector<RawLinConRow<T>> linconrows;
167 std::vector<T> lincon_b;
169 std::size_t nitems = 0;
170 std::vector<int> itemcap;
173 bool retrieval =
false;
175 Distrib<T> setuptime;
176 Distrib<T> delayofftime;
181 std::vector<T> lldscaling;
183 std::vector<T> cdscalingpeak;
185 std::vector<T> jdscalingpeak;
186 std::vector<RawServerPool<T>> pools;
204 double speed_factor = 1.0;
205 double quantum = 0.0;
207 bool is_host_class =
false;
229Distrib<T> host_demand(
const std::string& mean_s,
const std::string& scv_s) {
236 if constexpr (!num_traits<T>::has_transcendental) {
242 "lqn reader: an activity declares host-demand-cvsq " + scv_s +
243 ", whose APH / HyperExp fit needs a square root that exact arithmetic cannot "
244 "represent; solve this model in double precision");
262Distrib<T> setup_time(
const std::string& mean_s,
const std::string& scv_s) {
268 if constexpr (!num_traits<T>::has_transcendental) {
270 "lqn reader: a task declares a setup or delay-off scv " + scv_s +
271 ", whose APH fit needs a square root that exact arithmetic cannot represent; "
272 "solve this model in double precision");
290 for (std::size_t i = 0; i < s.size(); ++i) {
292 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r')
continue;
293 key.push_back(
static_cast<char>(std::toupper(
static_cast<unsigned char>(c))));
295 if (key.empty() || key ==
"FIFO")
return R::FIFO;
296 if (key ==
"RR" || key ==
"RANDOM")
return R::RR;
297 if (key ==
"SFIFO")
return R::SFIFO;
298 if (key ==
"LRU")
return R::LRU;
299 if (key ==
"HLRU")
return R::HLRU;
300 if (key ==
"CLIMB")
return R::CLIMB;
301 if (key ==
"QLRU")
return R::QLRU;
302 throw UnsupportedError(
"lqn reader: unsupported cache replacement strategy '" + s +
"'");
316std::vector<T> popularity_from_lqnx(
const xml::Element* item, std::size_t cardinality) {
318 const std::vector<const xml::Element*> pops = item->child_tags(
"access-popularity");
323 for (std::size_t k = 0; k < cardinality; ++k)
324 out.push_back(num_traits<T>::from_double(1.0 /
double(cardinality ? cardinality : 1)));
327 const xml::Element* pe = pops[0];
328 const std::string family = pe->attr(
"name");
329 std::vector<std::string> raw;
330 for (
const xml::Element* par : pe->child_tags(
"parameter")) raw.push_back(par->attr(
"value"));
331 if (family ==
"Zipf") {
334 "lqn reader: an access popularity of class Zipf carries exactly two parameters, "
335 "the shape s then the item count n, but this one carries " +
336 std::to_string(raw.size()));
338 const std::size_t n =
static_cast<std::size_t
>(
dbl_from_decimal(raw[1],
double(cardinality)));
340 for (std::size_t k = 1; k <= n; ++k) h += std::pow(
double(k), -s);
341 for (std::size_t k = 1; k <= n; ++k)
342 out.push_back(num_traits<T>::from_double(std::pow(
double(k), -s) / h));
345 if (family.empty() || family ==
"DiscreteSampler") {
346 const std::size_t n =
347 (cardinality > 0 && raw.size() == 2 * cardinality) ? cardinality : raw.size();
352 "lqn reader: an access popularity is written as '" + family +
353 "', and the discrete families the .lqnx dialect carries are DiscreteSampler and Zipf");
361 const std::string& act_name) {
363 for (std::size_t i = 0; i < name.size(); ++i) {
364 const char c = name[i];
365 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r')
continue;
366 key +=
static_cast<char>(std::toupper(
static_cast<unsigned char>(c)));
370 throw InputError(
"lqn reader: activity '" + act_name +
371 "' declares a call group with an unrecognized strategy '" + name +
372 "'; the dialect spells them RROBIN and JSQ");
383void read_call_groups(
const xml::Element* ae, RawActivity<T>& ac) {
384 const std::vector<const xml::Element*> groups = ae->child_tags(
"call-group");
385 for (std::size_t g = 0; g < groups.size(); ++g) {
386 std::vector<std::string> dests;
387 const std::vector<const xml::Element*> de = groups[g]->child_tags(
"dest");
388 for (std::size_t d = 0; d < de.size(); ++d) dests.push_back(de[d]->attr(
"name"));
389 ac.call_groups.push_back(
390 std::make_pair(callgroup_from_lqnx(groups[g]->attr(
"strategy"), ac.name), dests));
413 std::vector<detail::RawTask<T>>
tasks;
415 std::vector<detail::RawActivity<T>>
acts;
425 std::map<std::size_t, std::pair<Matrix<T>, std::vector<T>>>
proc_lincon;
439 std::map<std::size_t, std::vector<detail::RawServerPool<T>>>
proc_pools;
447 const std::vector<detail::RawProc>& procs = m.
procs;
448 const std::vector<detail::RawTask<T>>& tasks = m.
tasks;
449 const std::vector<detail::RawEntry<T>>& entries = m.
entries;
450 const std::vector<detail::RawActivity<T>>& acts = m.
acts;
457 l.
nacts = acts.size();
465 const std::size_t N = l.
nidx;
467 l.
names.assign(N + 1, {});
469 l.
type.assign(N + 1, LqnElement::HOST);
470 l.
parent.assign(N + 1, 0);
471 l.
sched.assign(NT + 1, SchedStrategy::NONE);
472 l.
mult.assign(NT + 1, 0.0);
474 l.
repl.assign(NT + 1, 1.0);
475 l.
prio.assign(NT + 1, 0);
482 l.
isref.assign(NT + 1,
false);
483 l.
iscache.assign(NT + 1,
false);
486 l.
nitems.assign(N + 1, 0);
502 l.
actpretype.assign(N + 1, PrecedenceType::NONE);
512 std::unordered_map<std::string, std::size_t> byhash;
514 for (std::size_t p = 0; p < l.
nhosts; ++p) {
515 const std::size_t idx = p + 1;
516 l.
sched[idx] = procs[p].sched;
517 l.
mult[idx] = procs[p].mult;
518 l.
repl[idx] = procs[p].repl;
519 l.
names[idx] = procs[p].name;
521 l.
type[idx] = LqnElement::HOST;
531 for (std::size_t t = 0; t < l.
ntasks; ++t) {
532 const std::size_t idx = l.
tshift + t + 1;
533 l.
sched[idx] = tasks[t].sched;
535 l.
think[idx] = tasks[t].thinktime;
536 l.
mult[idx] = tasks[t].mult;
537 l.
repl[idx] = tasks[t].repl;
538 l.
prio[idx] = tasks[t].priority;
544 l.
names[idx] = tasks[t].name;
547 l.
nitems[idx] = tasks[t].nitems;
548 l.
itemcap[idx] = tasks[t].itemcap;
550 l.
iscache[idx] = tasks[t].nitems > 0;
556 l.
hassetup[idx] = !tasks[t].setuptime.disabled &&
561 : tasks[t].sched == SchedStrategy::REF ?
"R:"
564 l.
parent[idx] = tasks[t].proc_slot + 1;
566 l.
type[idx] = LqnElement::TASK;
570 for (std::size_t t = 0; t < l.
ntasks; ++t) {
571 const std::size_t tidx = l.
tshift + t + 1;
579 std::unordered_map<std::string, std::size_t> task_by_name;
580 for (std::size_t t = 0; t < l.
ntasks; ++t) {
581 const std::size_t tidx = l.
tshift + t + 1;
582 task_by_name[l.
names[tidx]] = tidx;
584 for (std::size_t t = 0; t < l.
ntasks; ++t) {
585 const std::size_t tidx = l.
tshift + t + 1;
586 for (std::size_t k = 0; k < tasks[t].fanout.size(); ++k) {
587 const std::unordered_map<std::string, std::size_t>::const_iterator it =
588 task_by_name.find(tasks[t].fanout[k].first);
589 if (it != task_by_name.end())
590 l.
fanout[std::make_pair(tidx, it->second)] = tasks[t].fanout[k].second;
592 for (std::size_t k = 0; k < tasks[t].fanin.size(); ++k) {
593 const std::unordered_map<std::string, std::size_t>::const_iterator it =
594 task_by_name.find(tasks[t].fanin[k].first);
595 if (it != task_by_name.end())
596 l.
fanin[std::make_pair(tidx, it->second)] = tasks[t].fanin[k].second;
600 for (std::size_t p = 1; p <= l.
nhosts; ++p)
601 for (std::size_t idx = 1; idx <= NT; ++idx)
602 if (l.
type[idx] == LqnElement::TASK && l.
parent[idx] == p) l.
tasksof[p].push_back(idx);
604 for (std::size_t e = 0; e < l.
nentries; ++e) {
605 const std::size_t idx = l.
eshift + e + 1;
606 l.
names[idx] = entries[e].name;
608 l.
nitems[idx] = entries[e].cardinality;
609 l.
itemproc[idx] = entries[e].popularity;
610 l.
hashnames[idx] = (entries[e].cardinality > 0 ?
"I:" :
"E:") + entries[e].name;
613 l.
arrival[idx] = entries[e].arrival;
614 const std::size_t tidx = l.
tshift + entries[e].task_slot + 1;
616 l.
graph.set(tidx, idx, one);
618 l.
type[idx] = LqnElement::ENTRY;
621 for (std::size_t a = 0; a < l.
nacts; ++a) {
622 const std::size_t idx = l.
ashift + a + 1;
623 l.
names[idx] = acts[a].name;
625 l.
hostdem[idx] = acts[a].hostdem;
626 l.
actthink[idx] = acts[a].thinktime;
627 const std::size_t tidx = l.
tshift + acts[a].task_slot + 1;
629 l.
actsof[tidx].push_back(idx);
630 l.
type[idx] = LqnElement::ACTIVITY;
635 auto find_entry = [&](
const std::string& name) -> std::size_t {
636 auto it = byhash.find(
"E:" + name);
637 if (it != byhash.end())
return it->second;
638 it = byhash.find(
"I:" + name);
639 return it == byhash.end() ? 0 : it->second;
641 auto find_act = [&](
const std::string& name) -> std::size_t {
642 auto it = byhash.find(
"A:" + name);
643 return it == byhash.end() ? 0 : it->second;
647 std::vector<std::pair<std::size_t, std::size_t>> loop_back_edges;
648 std::unordered_map<std::string, std::string> bound_entry_to_act;
649 std::size_t cidx = 0;
651 auto add_call = [&](std::size_t src, std::size_t dst_e,
CallType ct,
const T& mean,
652 const std::string& arrow) {
664 l.
calltype.push_back(CallType::NONE);
669 for (std::size_t t = 0; t < l.
ntasks; ++t) {
670 const std::size_t tidx = l.
tshift + t + 1;
671 for (std::size_t a = 0; a < l.
nacts; ++a) {
672 if (acts[a].task_slot != t)
continue;
673 const std::size_t aidx = l.
ashift + a + 1;
675 if (!acts[a].bound_to_entry.empty()) {
676 const std::size_t eidx = find_entry(acts[a].bound_to_entry);
678 l.
graph.set(eidx, aidx, one);
679 auto it = bound_entry_to_act.find(acts[a].bound_to_entry);
680 if (it != bound_entry_to_act.end())
681 throw InputError(
"lqn reader: activities '" + it->second +
"' and '" +
682 acts[a].name +
"' are both bound to entry '" +
683 acts[a].bound_to_entry +
"'");
684 bound_entry_to_act[acts[a].bound_to_entry] = acts[a].name;
688 for (
const auto& c : acts[a].sync_calls) {
689 const std::size_t te = find_entry(c.dest);
691 throw InputError(
"lqn reader: activity '" + acts[a].name +
692 "' calls unknown entry '" + c.dest +
"'");
693 const std::size_t tt = l.
parent[te];
695 throw InputError(
"lqn reader: an entry on a task cannot call another entry on "
696 "the same task ('" + acts[a].name +
"' -> '" + c.dest +
"')");
697 add_call(aidx, te, CallType::SYNC, c.mean,
"=>");
698 l.
callsof[aidx].push_back(cidx);
708 l.
graph.set(aidx, te, one);
710 for (
const auto& c : acts[a].async_calls) {
711 const std::size_t te = find_entry(c.dest);
713 throw InputError(
"lqn reader: activity '" + acts[a].name +
714 "' has an async call to unknown entry '" + c.dest +
"'");
715 const std::size_t tt = l.
parent[te];
717 throw InputError(
"lqn reader: async self-call from '" + acts[a].name +
"'");
718 add_call(aidx, te, CallType::ASYNC, c.mean,
"->");
719 l.
callsof[aidx].push_back(cidx);
729 l.
graph.set(aidx, te, one);
735 for (
const auto& g : acts[a].call_groups) {
739 for (
const std::string& nm : g.second) {
740 const std::size_t te = find_entry(nm);
742 throw InputError(
"lqn reader: activity '" + acts[a].name +
743 "' dispatches a call group to unknown entry '" + nm +
751 for (
const auto& pr : tasks[t].precedences) {
764 if (pr.pretype == PrecedenceType::PRE_AND && pr.has_quorum &&
767 bool resolved =
true;
768 for (
const std::string& nm : pr.preacts) {
769 const std::size_t ai = find_act(nm);
770 if (ai == 0) { resolved =
false;
break; }
773 for (
const std::string& nm : pr.postacts) {
774 const std::size_t ai = find_act(nm);
775 if (ai == 0) { resolved =
false;
break; }
780 std::size_t quorum_count = 0;
781 if (pr.pretype == PrecedenceType::PRE_AND) {
782 if (pr.preacts.empty())
783 throw InputError(
"lqn reader: PRE_AND precedence with no pre activities in "
784 "task '" + tasks[t].name +
"'");
785 quorum_count = (pr.has_quorum && pr.quorum >= 1 && pr.quorum <= pr.preacts.size())
789 for (std::size_t pa = 0; pa < pr.preacts.size(); ++pa) {
790 const std::size_t preaidx = find_act(pr.preacts[pa]);
792 throw InputError(
"lqn reader: precedence names unknown activity '" +
793 pr.preacts[pa] +
"'");
794 switch (pr.posttype) {
795 case PrecedenceType::POST_OR:
796 for (std::size_t po = 0; po < pr.postacts.size(); ++po) {
797 const std::size_t postaidx = find_act(pr.postacts[po]);
798 l.
graph.set(preaidx, postaidx, pr.postparams[po]);
803 case PrecedenceType::POST_AND:
804 for (std::size_t po = 0; po < pr.postacts.size(); ++po) {
805 const std::size_t postaidx = find_act(pr.postacts[po]);
806 l.
graph.set(preaidx, postaidx, one);
811 case PrecedenceType::POST_LOOP: {
813 const T counts = pr.postparams.empty() ? one : pr.postparams[0];
814 const std::size_t enda = pr.postacts.size() - 1;
815 const std::size_t loopentry = find_act(pr.preacts[0]);
816 const std::size_t loopstart = find_act(pr.postacts[0]);
817 const std::size_t loopend = find_act(pr.postacts[enda]);
819 l.
graph.set(loopentry, loopstart, counts);
820 l.
graph.set(loopentry, loopend, T(one - counts));
821 std::size_t cur = loopstart;
822 for (std::size_t po = 1; po + 1 < pr.postacts.size(); ++po) {
823 const std::size_t pi = find_act(pr.postacts[po]);
824 l.
graph.set(cur, pi, one);
828 l.
graph.set(cur, loopend, one);
831 std::size_t cur = loopentry;
832 for (std::size_t po = 0; po + 1 < pr.postacts.size(); ++po) {
833 const std::size_t pi = find_act(pr.postacts[po]);
834 l.
graph.set(cur, pi, one);
838 loop_back_edges.emplace_back(cur, loopstart);
839 l.
graph.set(cur, loopstart, T(one - one / counts));
840 l.
graph.set(cur, loopend, T(one / counts));
846 for (std::size_t po = 0; po < pr.postacts.size(); ++po) {
847 const std::size_t postaidx = find_act(pr.postacts[po]);
849 throw InputError(
"lqn reader: precedence names unknown activity '" +
850 pr.postacts[po] +
"'");
851 l.
graph.set(preaidx, postaidx, one);
854 if (quorum_count > 0) l.
actquorum[postaidx] = quorum_count;
863 for (std::size_t e = 0; e < l.
nentries; ++e) {
864 const std::size_t eidx = l.
eshift + e + 1;
865 const std::size_t src_t = l.
parent[eidx];
866 for (std::size_t f = 0; f < entries[e].fwd_dest.size(); ++f) {
867 const std::size_t te = find_entry(entries[e].fwd_dest[f]);
869 throw InputError(
"lqn reader: entry '" + entries[e].name +
870 "' forwards to unknown entry '" + entries[e].fwd_dest[f] +
"'");
871 if (l.
parent[te] == src_t)
872 throw InputError(
"lqn reader: entry '" + entries[e].name +
873 "' forwards to an entry on the same task");
874 add_call(eidx, te, CallType::FWD, entries[e].fwd_prob[f],
"~>");
876 l.
graph.set(eidx, te, one);
886 for (std::size_t cidx2 = 1; cidx2 <= l.
tshift + l.
ntasks; ++cidx2) {
887 const bool ishost = cidx2 <= l.
nhosts;
888 const std::vector<std::size_t>& colIdx =
890 const char* colwhat = ishost ?
"tasks on this host" :
"entries of this task";
892 const std::vector<T>* rawb =
nullptr;
893 const std::vector<detail::RawLinConRow<T>>* rows =
nullptr;
895 typename std::map<std::size_t, std::vector<detail::RawLinConRow<T>>>::const_iterator
898 typename std::map<std::size_t,
899 std::pair<Matrix<T>, std::vector<T>>>::const_iterator ip =
902 rawA = &ip->second.first;
903 rawb = &ip->second.second;
906 const detail::RawTask<T>& rt = tasks[cidx2 - l.
tshift - 1];
907 rows = &rt.linconrows;
911 const std::size_t ncols = colIdx.size();
912 std::vector<std::vector<T>> Arows;
913 std::vector<T> brows;
914 if (rawA !=
nullptr && rawA->
rows() > 0) {
915 if (rawA->
cols() != ncols)
916 throw InputError(
"lqn reader: admission constraint on '" + l.
names[cidx2] +
917 "' has " + std::to_string(rawA->
cols()) +
" columns but there are " +
918 std::to_string(ncols) +
" " + colwhat);
919 for (std::size_t k = 0; k < rawA->
rows(); ++k) {
920 std::vector<T> row(ncols, zero);
921 for (std::size_t j = 0; j < ncols; ++j) row[j] = (*rawA)(k, j);
922 Arows.push_back(row);
923 brows.push_back(k < rawb->size() ? (*rawb)[k] : zero);
926 if (rows !=
nullptr) {
927 for (std::size_t r = 0; r < rows->size(); ++r) {
928 std::vector<T> row(ncols, zero);
929 for (std::size_t k = 0; k < (*rows)[r].names.size(); ++k) {
930 std::size_t pos = ncols;
931 for (std::size_t j = 0; j < ncols; ++j)
932 if (l.
names[colIdx[j]] == (*rows)[r].names[k]) pos = j;
934 throw InputError(
"lqn reader: admission constraint on '" + l.
names[cidx2] +
935 "' names '" + (*rows)[r].names[k] +
936 "', which is not one of the " + colwhat);
937 row[pos] = T(row[pos] + (*rows)[r].coeffs[k]);
939 Arows.push_back(row);
940 brows.push_back((*rows)[r].cap);
943 if (Arows.empty())
continue;
945 for (std::size_t k = 0; k < Arows.size(); ++k)
946 for (std::size_t j = 0; j < ncols; ++j) A(k, j) = Arows[k][j];
955 for (std::size_t pidx = 1; pidx <= l.
tshift + l.
ntasks; ++pidx) {
956 const bool ishost = pidx <= l.
nhosts;
957 const std::vector<std::size_t>& colIdx =
959 const char* colwhat = ishost ?
"tasks on this host" :
"entries of this task";
960 const std::vector<detail::RawServerPool<T>>* raw =
nullptr;
962 typename std::map<std::size_t,
963 std::vector<detail::RawServerPool<T>>>::const_iterator it =
965 if (it != m.
proc_pools.end()) raw = &it->second;
967 raw = &tasks[pidx - l.
tshift - 1].pools;
969 if (raw ==
nullptr || raw->empty())
continue;
970 const std::size_t ncols = colIdx.size();
973 "' but the element has no operand to serve");
976 for (std::size_t t2 = 0; t2 < raw->size(); ++t2) {
977 const detail::RawServerPool<T>& rp = (*raw)[t2];
978 sp.
names.push_back(rp.name);
979 sp.
counts.push_back(rp.count);
980 sp.
rates.push_back(rp.rate);
981 for (std::size_t k = 0; k < rp.compatible.size(); ++k) {
983 for (std::size_t j = 0; j < ncols; ++j) {
984 if (l.
names[colIdx[j]] == rp.compatible[k]) {
991 throw InputError(
"lqn reader: server pool '" + rp.name +
"' on '" +
992 l.
names[pidx] +
"' names '" + rp.compatible[k] +
993 "', which is not one of the " + colwhat);
998 for (std::size_t j = 0; j < ncols; ++j) {
1000 for (std::size_t t2 = 0; t2 < sp.
npools(); ++t2)
1001 if (sp.
compat(t2, j) != zero) served =
true;
1004 "' is compatible with no server pool, so it can never be served");
1010 for (std::size_t e = 1; e <= l.
nentries; ++e) {
1011 const std::size_t eidx = l.
eshift + e;
1013 for (std::size_t s : l.
graph.succ(eidx))
1014 if (s > l.
ashift) bound =
true;
1017 if (!bound)
throw InputError(
"An entry does not have any boundTo activity.");
1026 for (std::size_t e = 0; e < entries.size(); ++e) {
1027 for (
const std::string& rname : entries[e].reply_activities) {
1028 const std::size_t aidx = find_act(rname);
1029 if (aidx == 0)
continue;
1030 for (std::size_t succ : l.
graph.succ(aidx)) {
1031 if (succ <= l.
ashift)
continue;
1033 throw InputError(
"Unsupported replyTo in non-terminal activity.");
1039 for (std::size_t tidx = 1; tidx <= NT; ++tidx) {
1040 if (l.
sched[tidx] != SchedStrategy::INF)
continue;
1041 if (l.
type[tidx] != LqnElement::TASK)
continue;
1043 for (std::size_t c = 1; c <= NT; ++c)
1048 for (std::size_t idx = 1; idx <= NT; ++idx) l.
isref[idx] = l.
sched[idx] == SchedStrategy::REF;
1052 for (std::size_t i = 1; i <= N; ++i) {
1053 if (l.
type[i] != LqnElement::TASK || l.
isref[i])
continue;
1054 std::vector<std::size_t> to_flip;
1055 for (
const auto& e : l.
dag.row[i])
1056 if (l.
type[e.first] == LqnElement::ENTRY && e.second != zero)
1057 to_flip.push_back(e.first);
1058 for (std::size_t j : to_flip) {
1060 l.
dag.set(j, i, one);
1063 for (
const auto& be : loop_back_edges) l.
dag.erase(be.first, be.second);
1066 for (std::size_t e = 1; e <= l.
nentries; ++e) {
1067 const std::size_t eidx = l.
eshift + e;
1068 const std::size_t tidx = l.
parent[eidx];
1069 std::vector<bool> visited(N + 1,
false);
1070 std::vector<std::size_t> stack{eidx};
1071 visited[eidx] =
true;
1072 while (!stack.empty()) {
1073 const std::size_t v = stack.back();
1075 for (std::size_t w : l.
graph.succ(v))
1081 std::vector<std::size_t> found;
1082 for (std::size_t i = 1; i <= N; ++i)
1083 if (visited[i] && l.
type[i] == LqnElement::ACTIVITY && l.
parent[i] == tidx)
1092 for (std::size_t i = 1; i <= N; ++i)
1093 for (
const auto& ed : l.
dag.row[i])
1094 if (ed.second != zero) in.
dag(i - 1, ed.first - 1) = 1.0;
1097 in.
isref.assign(N,
false);
1106 for (std::size_t i = 1; i <= N; ++i) {
1107 const double m = i <= NT ? l.
mult[i] : std::numeric_limits<double>::infinity();
1110 switch (l.
type[i]) {
1121 for (std::size_t i = 1; i <= NT; ++i)
1122 l.
maxmult[i] = mm[i - 1].infinite ? std::numeric_limits<double>::infinity()
1127 for (std::size_t e = 1; e <= l.
nentries; ++e) {
1128 const std::size_t eidx = l.
eshift + e;
1129 bool sync =
false, async =
false;
1130 for (std::size_t c = 1; c <= l.
ncalls; ++c) {
1132 if (l.
calltype[c] == CallType::SYNC) sync =
true;
1133 if (l.
calltype[c] == CallType::ASYNC) async =
true;
1137 "' is called both synchronously and asynchronously");
1160inline std::string fmt_num(
double v) {
1162 std::snprintf(buf,
sizeof(buf),
"%g", v);
1163 return std::string(buf);
1167inline double attr_num(
const std::string& s,
double dflt) {
1168 if (s.empty())
return dflt;
1170 return std::stod(s);
1171 }
catch (
const std::exception&) {
1172 return std::numeric_limits<double>::quiet_NaN();
1177inline bool iequals(
const std::string& a,
const std::string& b) {
1178 if (a.size() != b.size())
return false;
1179 for (std::size_t i = 0; i < a.size(); ++i)
1180 if (std::tolower(
static_cast<unsigned char>(a[i])) !=
1181 std::tolower(
static_cast<unsigned char>(b[i])))
1187inline std::string call_order_from_text(
const std::string& s) {
1188 if (iequals(s,
"DETERMINISTIC"))
return "DETERMINISTIC";
1189 return "STOCHASTIC";
1202inline void validate_input_model(
const xml::Element& doc) {
1203 const double tol = 1e-6;
1204 std::vector<std::string> proc_names;
1205 std::vector<std::string> task_names;
1206 std::vector<std::string> entry_names;
1207 std::vector<std::string> entry_owner;
1208 std::vector<bool> is_ref_entry;
1209 std::vector<std::string> call_dests;
1210 std::vector<std::string> reply_entries;
1211 bool has_ref_task =
false;
1212 bool has_open_arrival =
false;
1214 for (
const xml::Element* pe : doc.by_tag(
"processor")) {
1215 const std::string proc_name = pe->attr(
"name");
1216 if (std::find(proc_names.begin(), proc_names.end(), proc_name) != proc_names.end())
1217 throw InputError(
"Duplicate processor name \"" + proc_name +
"\".");
1218 proc_names.push_back(proc_name);
1220 for (
const xml::Element* te : pe->by_tag(
"task")) {
1221 const std::string task_name = te->attr(
"name");
1222 if (std::find(task_names.begin(), task_names.end(), task_name) != task_names.end())
1223 throw InputError(
"Duplicate task name \"" + task_name +
"\".");
1224 task_names.push_back(task_name);
1225 const bool is_ref = iequals(te->attr(
"scheduling"),
"ref");
1226 has_ref_task = has_ref_task || is_ref;
1228 const std::vector<const xml::Element*> entry_els = te->by_tag(
"entry");
1229 if (entry_els.empty())
1230 throw InputError(
"Task \"" + task_name +
"\" has no entries.");
1231 for (
const xml::Element* ee : entry_els) {
1232 const std::string entry_name = ee->attr(
"name");
1233 if (std::find(entry_names.begin(), entry_names.end(), entry_name) !=
1235 throw InputError(
"Duplicate entry name \"" + entry_name +
"\".");
1236 entry_names.push_back(entry_name);
1237 entry_owner.push_back(task_name);
1238 is_ref_entry.push_back(is_ref);
1240 const double arrival_rate =
1241 attr_num(ee->attr(
"open-arrival-rate"), std::numeric_limits<double>::quiet_NaN());
1242 if (arrival_rate > 0.0) {
1243 has_open_arrival =
true;
1245 throw InputError(
"Entry \"" + entry_name +
"\" belongs to reference task \"" +
1246 task_name +
"\" and cannot have open arrivals.");
1249 const std::vector<const xml::Element*> fwd_els = ee->by_tag(
"forwarding");
1250 if (is_ref && !fwd_els.empty())
1251 throw InputError(
"Entry \"" + entry_name +
"\" belongs to reference task \"" +
1252 task_name +
"\" and cannot forward requests.");
1253 double fwd_total = 0.0;
1254 for (
const xml::Element* fe : fwd_els) {
1255 const double prob = attr_num(fe->attr(
"prob"), 1.0);
1256 if (std::isnan(prob) || prob < 0.0 || prob > 1.0)
1257 throw InputError(
"Forwarding from entry \"" + entry_name +
"\" to entry \"" +
1258 fe->attr(
"dest") +
"\" has an invalid probability of " +
1259 fmt_num(prob) +
".");
1262 if (fwd_total > 1.0 + tol)
1264 "\" has a total forwarding probability of " +
1265 fmt_num(fwd_total) +
".");
1269 std::vector<std::string> act_names;
1270 for (
const xml::Element* ae : te->by_tag(
"activity")) {
1271 if (ae->parent ==
nullptr)
continue;
1272 if (ae->parent->name !=
"task-activities" &&
1273 ae->parent->name !=
"entry-phase-activities")
1275 const std::string act_name = ae->attr(
"name");
1276 if (std::find(act_names.begin(), act_names.end(), act_name) != act_names.end())
1277 throw InputError(
"Duplicate activity name \"" + act_name +
"\" in task \"" +
1279 act_names.push_back(act_name);
1282 for (
const xml::Element* ce : te->by_tag(
"synch-call"))
1283 call_dests.push_back(ce->attr(
"dest"));
1284 for (
const xml::Element* ce : te->by_tag(
"asynch-call"))
1285 call_dests.push_back(ce->attr(
"dest"));
1286 for (
const xml::Element* fe : te->by_tag(
"forwarding"))
1287 call_dests.push_back(fe->attr(
"dest"));
1289 for (
const xml::Element* oe : te->by_tag(
"post-OR")) {
1290 double branch_total = 0.0;
1291 for (
const xml::Element* be : oe->by_tag(
"activity")) {
1292 const double prob = attr_num(be->attr(
"prob"), 1.0);
1293 if (std::isnan(prob) || prob < 0.0 || prob > 1.0)
1294 throw InputError(
"Activity \"" + be->attr(
"name") +
"\" in task \"" +
1295 task_name +
"\" has an invalid branch probability of " +
1296 fmt_num(prob) +
".");
1297 branch_total += prob;
1299 if (std::fabs(branch_total - 1.0) > tol)
1300 throw InputError(
"Branch probabilities of an OR-fork in task \"" + task_name +
1301 "\" sum to " + fmt_num(branch_total) +
" instead of 1.");
1304 for (
const xml::Element* re : te->by_tag(
"reply-entry"))
1305 reply_entries.push_back(re->attr(
"name"));
1309 for (
const std::string& dest : call_dests) {
1310 const std::vector<std::string>::const_iterator it =
1311 std::find(entry_names.begin(), entry_names.end(), dest);
1312 if (it == entry_names.end())
continue;
1313 const std::size_t idx =
static_cast<std::size_t
>(it - entry_names.begin());
1314 if (is_ref_entry[idx])
1315 throw InputError(
"Entry \"" + entry_names[idx] +
"\" belongs to reference task \"" +
1316 entry_owner[idx] +
"\" and cannot receive requests.");
1319 for (
const std::string& reply_name : reply_entries) {
1320 const std::vector<std::string>::const_iterator it =
1321 std::find(entry_names.begin(), entry_names.end(), reply_name);
1322 if (it == entry_names.end())
continue;
1323 const std::size_t idx =
static_cast<std::size_t
>(it - entry_names.begin());
1324 if (is_ref_entry[idx])
1325 throw InputError(
"Entry \"" + entry_names[idx] +
"\" belongs to reference task \"" +
1326 entry_owner[idx] +
"\" and cannot be replied to.");
1329 if (!has_ref_task && !has_open_arrival)
1330 throw InputError(
"The model has no reference task and no open arrivals.");
1339 std::vector<detail::RawProc>& procs = m.
procs;
1340 std::vector<detail::RawTask<T>>& tasks = m.
tasks;
1341 std::vector<detail::RawEntry<T>>& entries = m.
entries;
1342 std::vector<detail::RawActivity<T>>& acts = m.
acts;
1346 detail::validate_input_model(*doc);
1350 const std::vector<const xml::Element*> proc_els = doc->by_tag(
"processor");
1353 pr.name = pe->attr(
"name");
1354 const std::string psched = pe->attr(
"scheduling");
1359 if (pr.sched == SchedStrategy::INF) {
1362 pr.mult = std::numeric_limits<double>::infinity();
1366 const std::size_t proc_slot = procs.size();
1367 procs.push_back(pr);
1370 detail::RawTask<T> tk;
1371 tk.name = te->attr(
"name");
1372 const std::string tsched = te->attr(
"scheduling");
1377 if (!std::isnan(prio_d)) tk.priority =
static_cast<int>(prio_d);
1378 if (tk.sched == SchedStrategy::INF) {
1379 tk.mult = std::numeric_limits<double>::infinity();
1383 const std::string think_s = te->attr(
"think-time");
1387 tk.proc_slot = proc_slot;
1392 tk.fanout.push_back(
1393 std::make_pair(fe->attr(
"dest"),
dbl_from_decimal(fe->attr(
"value"), 1.0)));
1396 std::make_pair(fe->attr(
"source"),
dbl_from_decimal(fe->attr(
"value"), 1.0)));
1405 tk.setuptime = detail::setup_time<T>(se->attr(
"mean"), se->attr(
"scv"));
1407 tk.delayofftime = detail::setup_time<T>(se->attr(
"mean"), se->attr(
"scv"));
1417 const std::string items_s = ce->attr(
"items");
1419 tk.nitems = items_d > 0.0 ?
static_cast<std::size_t
>(items_d) : 1;
1420 tk.replacestrat = detail::replacement_from_lqnx(ce->attr(
"replacement"));
1422 const std::string rs = ce->attr(
"retrieval");
1423 tk.retrieval = detail::iequals(rs,
"true") || rs ==
"1";
1427 tk.itemcap.push_back(cap_d > 0.0 ?
static_cast<int>(cap_d) : 1);
1432 if (tk.itemcap.empty()) tk.itemcap.push_back(1);
1434 const std::size_t task_slot = tasks.size();
1435 tasks.push_back(tk);
1438 detail::RawEntry<T> en;
1439 en.name = ee->attr(
"name");
1440 en.task_slot = task_slot;
1441 const std::string arr_s = ee->attr(
"open-arrival-rate");
1442 if (!arr_s.empty()) {
1445 en.has_arrival =
true;
1450 en.fwd_dest.push_back(fe->attr(
"dest"));
1451 const std::string ps = fe->attr(
"prob");
1460 en.cardinality = card_d > 0.0 ?
static_cast<std::size_t
>(card_d) : 1;
1461 en.popularity = detail::popularity_from_lqnx<T>(ie2, en.cardinality);
1463 const std::size_t entry_slot = entries.size();
1464 entries.push_back(en);
1466 const std::vector<const xml::Element*> epa = ee->by_tag(
"entry-phase-activities");
1469 std::map<int, std::string> by_phase;
1470 for (
const xml::Element* ae : epa[0]->by_tag(
"activity")) {
1471 const int phase =
static_cast<int>(
dbl_from_decimal(ae->attr(
"phase"), 1.0));
1472 detail::RawActivity<T> ac;
1473 ac.name = ae->attr(
"name");
1474 ac.hostdem = detail::host_demand<T>(ae->attr(
"host-demand-mean"),
1475 ae->attr(
"host-demand-cvsq"));
1476 const std::string att = ae->attr(
"think-time");
1478 ac.thinktime = att_d > 0.0
1481 ac.bound_to_entry = phase == 1 ? entries[entry_slot].name : std::string();
1483 ac.call_order = detail::call_order_from_text(ae->attr(
"call-order"));
1484 ac.task_slot = task_slot;
1486 ac.sync_calls.push_back(
1489 ac.async_calls.push_back(
1491 detail::read_call_groups<T>(ae, ac);
1492 by_phase[phase] = ac.name;
1499 if (!by_phase.empty() && !by_phase.count(1)) {
1500 const std::string& en = entries[entry_slot].name;
1501 std::vector<std::string> taken;
1502 for (
const xml::Element* de : doc->by_tag(
"activity")) taken.push_back(de->attr(
"name"));
1503 std::string ph1 = en +
"_ph1";
1505 while (std::find(taken.begin(), taken.end(), ph1) != taken.end())
1506 ph1 = en +
"_ph1_" + std::to_string(++suffix);
1508 std::fprintf(stderr,
"[LINE] Warning: parseXML: Entry %s declares no phase-1 activity and %s_ph1 is "
1509 "already an activity; its synthetic phase-1 activity is named %s.\n",
1510 en.c_str(), en.c_str(), ph1.c_str());
1511 detail::RawActivity<T> ac;
1515 ac.bound_to_entry = entries[entry_slot].name;
1517 ac.task_slot = task_slot;
1518 by_phase[1] = ac.name;
1522 for (
auto it = by_phase.begin(); it != by_phase.end(); ++it) {
1523 auto nx = std::next(it);
1524 if (nx == by_phase.end())
break;
1525 detail::RawPrecedence<T> pr;
1526 pr.pretype = PrecedenceType::PRE_SEQ;
1527 pr.posttype = PrecedenceType::POST_SEQ;
1528 pr.preacts.push_back(it->second);
1529 pr.postacts.push_back(nx->second);
1530 tasks[task_slot].precedences.push_back(pr);
1532 if (!by_phase.empty() && by_phase.count(1))
1533 entries[entry_slot].reply_activities.push_back(by_phase[1]);
1537 const std::vector<const xml::Element*> tal = te->by_tag(
"task-activities");
1542 if (ae->parent != ta)
continue;
1543 detail::RawActivity<T> ac;
1544 ac.name = ae->attr(
"name");
1545 ac.hostdem = detail::host_demand<T>(ae->attr(
"host-demand-mean"),
1546 ae->attr(
"host-demand-cvsq"));
1547 const std::string att = ae->attr(
"think-time");
1551 ac.bound_to_entry = ae->attr(
"bound-to-entry");
1553 ac.call_order = detail::call_order_from_text(ae->attr(
"call-order"));
1554 ac.task_slot = task_slot;
1556 ac.sync_calls.push_back(
1559 ac.async_calls.push_back(
1561 detail::read_call_groups<T>(ae, ac);
1566 detail::RawPrecedence<T> pr;
1568 if (!pe2->child_tags(
"pre").empty()) {
1570 pr.pretype = PrecedenceType::PRE_SEQ;
1571 }
else if (!pe2->child_tags(
"pre-AND").empty()) {
1573 pr.pretype = PrecedenceType::PRE_AND;
1574 }
else if (!pe2->child_tags(
"pre-OR").empty()) {
1576 pr.pretype = PrecedenceType::PRE_OR;
1578 throw InputError(
"lqn reader: <precedence> without a pre element");
1581 pr.preacts.push_back(ae->attr(
"name"));
1582 if (pr.pretype == PrecedenceType::PRE_OR)
1585 if (pr.pretype == PrecedenceType::PRE_SEQ && pr.preacts.size() > 1)
1586 pr.preacts.resize(1);
1587 if (pr.pretype == PrecedenceType::PRE_AND) {
1588 const std::string q = pre->
attr(
"quorum");
1590 pr.has_quorum =
true;
1596 if (!pe2->child_tags(
"post").empty()) {
1598 pr.posttype = PrecedenceType::POST_SEQ;
1599 }
else if (!pe2->child_tags(
"post-AND").empty()) {
1601 pr.posttype = PrecedenceType::POST_AND;
1602 }
else if (!pe2->child_tags(
"post-OR").empty()) {
1604 pr.posttype = PrecedenceType::POST_OR;
1605 }
else if (!pe2->child_tags(
"post-LOOP").empty()) {
1607 pr.posttype = PrecedenceType::POST_LOOP;
1608 }
else if (!pe2->child_tags(
"post-CACHE").empty()) {
1610 pr.posttype = PrecedenceType::POST_CACHE;
1619 pr.postacts.push_back(ae->attr(
"name"));
1620 if (pr.posttype == PrecedenceType::POST_OR)
1622 if (pr.posttype == PrecedenceType::POST_LOOP)
1625 if (pr.posttype == PrecedenceType::POST_CACHE) {
1633 std::vector<std::string> hit, miss, unlabelled;
1634 const std::vector<const xml::Element*> aes = post->
by_tag(
"activity");
1635 for (std::size_t k = 0; k < aes.size(); ++k) {
1636 const std::string res = aes[k]->attr(
"cache-result");
1638 hit.push_back(pr.postacts[k]);
1639 else if (res ==
"miss")
1640 miss.push_back(pr.postacts[k]);
1642 unlabelled.push_back(pr.postacts[k]);
1644 if (!hit.empty() || !miss.empty()) {
1646 pr.postacts.insert(pr.postacts.end(), miss.begin(), miss.end());
1647 pr.postacts.insert(pr.postacts.end(), unlabelled.begin(),
1650 if (pr.postacts.size() < 2)
1652 "lqn reader: a <post-CACHE> branches on a hit and a miss, so it "
1653 "names two activities");
1655 if (pr.posttype == PrecedenceType::POST_LOOP)
1656 pr.postacts.push_back(post->
attr(
"end"));
1657 tasks[task_slot].precedences.push_back(pr);
1661 const std::string ename = re->attr(
"name");
1662 std::size_t slot = entries.size();
1663 for (std::size_t s = 0; s < entries.size(); ++s)
1664 if (entries[s].name == ename) {
1668 if (slot == entries.size())
1669 throw InputError(
"lqn reader: <reply-entry> names unknown entry '" + ename +
1672 entries[slot].reply_activities.push_back(ra->attr(
"name"));
UnsupportedError(const std::string &what)
Decimal literal -> T, without a detour through double when T is exact.
The moment fitters the reference distributions carry as STATIC FACTORIES: Erlang.fitMeanAndOrder,...
The exception types the port throws.
LayeredNetworkStruct, the flattened description of a layered queueing network.
Maximum sustainable multiplicity (concurrency level) of every element of a layered software network.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
PrecedenceType
Activity precedence kinds, with the values of MATLAB ActivityPrecedenceType.
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
SchedStrategy sched_from_lqnx(const std::string &s)
Parse the scheduling attribute of an .lqnx processor or task.
CallType
Call kinds, with the values of MATLAB CallType.
Distrib< T > aph_fit_mean_scv(const T &mean, const T &scv)
APH.fitMeanAndSCV(MEAN, SCV), through mam::aph_fit_mean_scv.
Distrib< T > hyperexp_fit_mean_scv(const T &mean, const T &scv)
HyperExp.fitMeanAndSCV(MEAN, SCV), which is map_hyperexp at p = 0.99 read back as (p,...
std::function< std::vector< T >(const std::vector< T > &)> CdScaling
A class-dependent scaling map, sn.cdscaling.
ReplacementStrategy
Cache replacement policies, with the values of MATLAB ReplacementStrategy.
LqnModel< T > read_lqnx_model(const std::string &path)
LqnStruct< T > lqn_finalize(const LqnModel< T > &m)
Port of @LayeredNetwork/getStruct.m: flatten the model into its struct.
LqnStruct< T > read_lqnx(const std::string &path)
Read a .lqnx model.
std::vector< Multiplicity< T > > lsn_max_multiplicity(const LsnInput< T > &lsn)
Maximum sustainable multiplicity (concurrency level) of every element of a layered software network.
std::unique_ptr< Element > parse_file(const std::string &path)
Read and parse a file.
Conservation laws of a layered queueing network, enumerated from its structure.
double dbl_from_decimal(const std::string &s, double fallback)
Parse a decimal literal as a plain double (multiplicities, populations, tolerances).
T num_from_decimal(const std::string &s)
Parse a decimal literal into T.
static constexpr double FineTol
void set(std::size_t i, std::size_t j)
void resize(std::size_t nn)
static Distrib exp_rate(const T &r)
static Distrib disabled_dist()
static Distrib det(const T &m)
static Distrib immediate()
The Immediate singleton.
static Distrib exp_mean(const T &m)
One routed call group: an activity, the strategy that picks among its targets, and the target ENTRIES...
std::vector< std::size_t > targets
absolute entry indices, in declaration order
lang::RoutingStrategy strategy
std::size_t caller
absolute index of the dispatching activity
The intermediate model, and the second stage that flattens it.
std::vector< detail::RawTask< T > > tasks
std::vector< detail::RawActivity< T > > acts
std::map< std::size_t, std::vector< T > > proc_jdscalingpeak
std::map< std::size_t, std::vector< detail::RawServerPool< T > > > proc_pools
std::vector< detail::RawProc > procs
std::map< std::size_t, CdScaling< T > > proc_jdscaling
std::map< std::size_t, std::vector< T > > proc_cdscalingpeak
std::map< std::size_t, std::pair< Matrix< T >, std::vector< T > > > proc_lincon
std::map< std::size_t, std::vector< detail::RawLinConRow< T > > > proc_linconrows
Admission constraints declared on a HOST, by 0-based processor slot.
std::map< std::size_t, CdScaling< T > > proc_cdscaling
std::string name
LayeredNetwork.getName(); empty when unnamed.
std::map< std::size_t, std::vector< T > > proc_lldscaling
Queue-dependent service rates and compatibility pools declared on a HOST, by 0-based processor slot.
std::vector< detail::RawEntry< T > > entries
One activity precedence of a task, with its activities resolved to indices.
std::vector< std::size_t > preacts
absolute activity indices
std::vector< T > preparams
PRE_OR shares, or a PRE_AND quorum.
std::vector< T > postparams
POST_OR probabilities or the POST_LOOP count.
std::vector< std::size_t > postacts
absolute activity indices
std::vector< Distrib< T > > hostdem
(nidx+1) host demand per activity (Immediate elsewhere)
std::vector< std::vector< T > > jdscalingpeak
(tshift+ntasks+1)
std::vector< int > actphase
(nacts+1) phase of each activity, 1-based by act
std::vector< CallType > calltype
(ncalls+1)
std::vector< std::size_t > callpair_dst
(ncalls+1) called entry
std::vector< std::vector< LqnPrecedence< T > > > precedences
Activity precedences of each task, as DECLARED, indexed by the task's absolute index.
std::vector< std::string > callhashnames
(ncalls+1)
std::vector< Distrib< T > > setuptime
Setup tasks: the server powers down when idle and pays to restart.
std::map< std::pair< std::size_t, std::size_t >, double > fanout
Fan-out and fan-in, keyed by task element index, absent = 0.
std::vector< bool > hassetup
(tshift+ntasks+1)
std::vector< LqnElement > type
(nidx+1)
std::vector< std::size_t > nitems
Cache tasks and item entries.
std::vector< Distrib< T > > actthink
(nidx+1) activity think time
std::vector< std::vector< T > > lldscaling
Queue-dependent service rates declared on a layer server (a host or a task), by element index,...
std::vector< double > mult
(tshift+ntasks+1) declared multiplicity, may be Inf
std::vector< SchedStrategy > sched
(tshift+ntasks+1)
std::vector< std::size_t > callpair_src
(ncalls+1) calling activity (entry for FWD)
std::vector< std::size_t > parent
(nidx+1) host of a task, task of an entry/activity
std::vector< std::vector< T > > lincon_b
std::vector< ServerPools< T > > pools
(tshift+ntasks+1)
std::vector< T > callproc_mean
(ncalls+1) mean number of calls
std::vector< std::size_t > actquorum
(nidx+1) AND-join quorum, on the join target
std::vector< bool > iscache
(tshift+ntasks+1)
std::vector< bool > has_arrival
(nidx+1) entry with an open arrival
std::vector< std::vector< std::size_t > > callsof
(nidx+1) call indices issued by an activity
std::vector< std::string > hashnames
(nidx+1) name prefixed by kind: P:/T:/R:/E:/A:
std::vector< std::string > callnames
(ncalls+1)
std::vector< CdScaling< T > > cdscaling
(tshift+ntasks+1)
std::vector< std::vector< int > > itemcap
(tshift+ntasks+1)
std::vector< std::vector< std::size_t > > entriesof
(tshift+ntasks+1)
std::vector< bool > hasretrieval
(tshift+ntasks+1) cache task with delayed-hit retrieval
std::vector< double > repl
(tshift+ntasks+1) replication
std::vector< std::string > names
(nidx+1) declared name
std::vector< Matrix< T > > lincon_A
Admission constraint A n <= b on the layer station of a host or task.
SparseGraph< T > dag
graph with entry-task edges reversed and loop back-edges removed
std::vector< Distrib< T > > think
(nidx+1) task think time
std::vector< std::vector< std::size_t > > tasksof
(nhosts+1)
std::vector< bool > isref
(tshift+ntasks+1)
std::vector< PrecedenceType > actpretype
(nidx+1)
std::vector< std::vector< std::size_t > > actsof
(ashift+1) by task and by entry
std::map< std::pair< std::size_t, std::size_t >, double > fanin
std::vector< PrecedenceType > actposttype
(nidx+1)
std::vector< LqnCallGroup > callgroups
Synchronous calls DISPATCHED AS A GROUP, lsn.callgroups.
std::vector< std::vector< T > > cdscalingpeak
(tshift+ntasks+1)
std::vector< Distrib< T > > delayofftime
std::vector< int > prio
(tshift+ntasks+1) task priority, larger first (lqns); 0 on hosts (getStruct.m)
std::vector< Distrib< T > > arrival
(nidx+1) open arrival process of an entry
std::vector< std::vector< T > > itemproc
(nidx+1) popularity pmf
std::vector< CdScaling< T > > jdscaling
(tshift+ntasks+1)
std::vector< ReplacementStrategy > replacestrat
(tshift+ntasks+1)
std::vector< double > maxmult
(tshift+ntasks+1) sustainable multiplicity
SparseGraph< T > graph
element call/precedence graph, edge weights are branch shares
SparseGraph< T > taskgraph
task-to-task calls
Heterogeneous server pools declared on a layer server, the twin of the nservertypes / servertypenames...
Matrix< T > compat
(npools x noperands), nonzero = eligible
std::vector< double > counts
(npools) servers held by each pool
std::vector< std::string > names
(npools) declared pool name
std::vector< T > rates
(npools) per-pool rate multiplier
std::size_t npools() const
static Multiplicity finite(const T &v)
static Multiplicity inf()
std::vector< const Element * > by_tag(const std::string &tag) const
Descendant-or-self search excluding self, in document order.
std::string attr(const std::string &key) const
Attribute value, or the empty string when absent (org.w3c.dom semantics).
std::vector< const Element * > child_tags(const std::string &tag) const
Direct children with the given tag, in document order.
A minimal XML DOM: read for the .lqnx interchange format, write for the JMT .jsimg and ....