5#ifndef LINE_LANG_LQN_LQN_READER_H
6#define LINE_LANG_LQN_LQN_READER_H
51#include <unordered_map>
79 std::string bound_to_entry;
82 std::string call_order =
"STOCHASTIC";
83 std::size_t task_slot = 0;
84 std::vector<RawCall<T>> sync_calls;
85 std::vector<RawCall<T>> async_calls;
92 std::vector<std::pair<lang::RoutingStrategy, std::vector<std::string>>> call_groups;
99 std::vector<std::string> preacts;
100 std::vector<std::string> postacts;
101 std::vector<T> preparams;
102 std::vector<T> postparams;
103 bool has_quorum =
false;
104 std::size_t quorum = 0;
110 std::size_t task_slot = 0;
111 std::vector<std::string> reply_activities;
112 bool has_arrival =
false;
114 std::vector<std::string> fwd_dest;
115 std::vector<T> fwd_prob;
117 std::size_t cardinality = 0;
118 std::vector<T> popularity;
120 std::string type =
"PH1PH2";
132 std::vector<std::string> names;
133 std::vector<T> coeffs;
145struct RawServerPool {
149 std::vector<std::string> compatible;
160 Distrib<T> thinktime;
161 std::size_t proc_slot = 0;
163 std::vector<std::pair<std::string, double>> fanout;
164 std::vector<std::pair<std::string, double>> fanin;
165 std::vector<RawPrecedence<T>> precedences;
166 std::vector<RawLinConRow<T>> linconrows;
168 std::vector<T> lincon_b;
170 std::size_t nitems = 0;
171 std::vector<int> itemcap;
174 bool retrieval =
false;
176 Distrib<T> setuptime;
177 Distrib<T> delayofftime;
182 std::vector<T> lldscaling;
184 std::vector<T> cdscalingpeak;
186 std::vector<T> jdscalingpeak;
187 std::vector<RawServerPool<T>> pools;
205 double speed_factor = 1.0;
207 double quantum = 0.001;
209 bool is_host_class =
false;
231Distrib<T> host_demand(
const std::string& mean_s,
const std::string& scv_s) {
238 if constexpr (!num_traits<T>::has_transcendental) {
244 "lqn reader: an activity declares host-demand-cvsq " + scv_s +
245 ", whose APH / HyperExp fit needs a square root that exact arithmetic cannot "
246 "represent; solve this model in double precision");
264Distrib<T> setup_time(
const std::string& mean_s,
const std::string& scv_s) {
270 if constexpr (!num_traits<T>::has_transcendental) {
272 "lqn reader: a task declares a setup or delay-off scv " + scv_s +
273 ", whose APH fit needs a square root that exact arithmetic cannot represent; "
274 "solve this model in double precision");
292 for (std::size_t i = 0; i < s.size(); ++i) {
294 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r')
continue;
295 key.push_back(
static_cast<char>(std::toupper(
static_cast<unsigned char>(c))));
297 if (key.empty() || key ==
"FIFO")
return R::FIFO;
298 if (key ==
"RR" || key ==
"RANDOM")
return R::RR;
299 if (key ==
"SFIFO")
return R::SFIFO;
300 if (key ==
"LRU")
return R::LRU;
301 if (key ==
"HLRU")
return R::HLRU;
302 if (key ==
"CLIMB")
return R::CLIMB;
303 if (key ==
"QLRU")
return R::QLRU;
304 throw UnsupportedError(ErrorKind{
"Import"},
"lqn reader: unsupported cache replacement strategy '" + s +
"'");
318std::vector<T> popularity_from_lqnx(
const xml::Element* item, std::size_t cardinality) {
320 const std::vector<const xml::Element*> pops = item->child_tags(
"access-popularity");
325 for (std::size_t k = 0; k < cardinality; ++k)
326 out.push_back(num_traits<T>::from_double(1.0 /
double(cardinality ? cardinality : 1)));
329 const xml::Element* pe = pops[0];
330 const std::string family = pe->attr(
"name");
331 std::vector<std::string> raw;
332 for (
const xml::Element* par : pe->child_tags(
"parameter")) raw.push_back(par->attr(
"value"));
333 if (family ==
"Zipf") {
336 "lqn reader: an access popularity of class Zipf carries exactly two parameters, "
337 "the shape s then the item count n, but this one carries " +
338 std::to_string(raw.size()));
340 const std::size_t n =
static_cast<std::size_t
>(
dbl_from_decimal(raw[1],
double(cardinality)));
342 for (std::size_t k = 1; k <= n; ++k) h += std::pow(
double(k), -s);
343 for (std::size_t k = 1; k <= n; ++k)
344 out.push_back(num_traits<T>::from_double(std::pow(
double(k), -s) / h));
347 if (family.empty() || family ==
"DiscreteSampler") {
348 const std::size_t n =
349 (cardinality > 0 && raw.size() == 2 * cardinality) ? cardinality : raw.size();
354 "lqn reader: an access popularity is written as '" + family +
355 "', and the discrete families the .lqnx dialect carries are DiscreteSampler and Zipf");
363 const std::string& act_name) {
365 for (std::size_t i = 0; i <
name.size(); ++i) {
366 const char c =
name[i];
367 if (c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r')
continue;
368 key +=
static_cast<char>(std::toupper(
static_cast<unsigned char>(c)));
372 throw InputError(ErrorKind{
"Format"},
"lqn reader: activity '" + act_name +
373 "' declares a call group with an unrecognized strategy '" +
name +
374 "'; the dialect spells them RROBIN and JSQ");
385void read_call_groups(
const xml::Element* ae, RawActivity<T>& ac) {
386 const std::vector<const xml::Element*> groups = ae->child_tags(
"call-group");
387 for (std::size_t g = 0; g < groups.size(); ++g) {
388 std::vector<std::string> dests;
389 const std::vector<const xml::Element*> de = groups[g]->child_tags(
"dest");
390 for (std::size_t d = 0; d < de.size(); ++d) dests.push_back(de[d]->attr(
"name"));
391 ac.call_groups.push_back(
392 std::make_pair(callgroup_from_lqnx(groups[g]->
attr(
"strategy"), ac.name), dests));
415 std::vector<detail::RawTask<T>>
tasks;
417 std::vector<detail::RawActivity<T>>
acts;
427 std::map<std::size_t, std::pair<Matrix<T>, std::vector<T>>>
proc_lincon;
441 std::map<std::size_t, std::vector<detail::RawServerPool<T>>>
proc_pools;
453 const std::size_t N = l.
nidx;
457 for (std::size_t i = 1; i <= N; ++i)
458 for (
const auto& ed : l.
dag.row[i])
459 if (ed.second != zero) in.dag(i - 1, ed.first - 1) = 1.0;
462 in.isref.assign(N,
false);
463 in.entry_has_arrival.assign(N,
false);
470 in.hassetup.assign(N,
false);
471 for (std::size_t i = 1; i <= N; ++i) {
472 const double m = i <= NT ? mult[i] : std::numeric_limits<double>::infinity();
481 if (i <= NT) in.isref[i - 1] = l.
isref[i];
482 if (i <= NT) in.hassetup[i - 1] = l.
hassetup[i];
487 in.repl.assign(N, 1L);
488 for (std::size_t i = 1; i <= N; ++i) {
489 const std::size_t owner = (i > NT && l.
parent[i] >= 1) ? l.
parent[i] : i;
490 if (owner <= NT && owner < l.
repl.size()) in.repl[i - 1] = std::max(1L, std::lround(l.
repl[owner]));
493 std::vector<double> out(NT + 1, 0.0);
494 for (std::size_t i = 1; i <= NT; ++i)
495 out[i] = mm[i - 1].infinite ? std::numeric_limits<double>::infinity() : mm[i - 1].value;
504 const std::vector<detail::RawProc>& procs = m.
procs;
505 const std::vector<detail::RawTask<T>>& tasks = m.
tasks;
506 const std::vector<detail::RawEntry<T>>& entries = m.
entries;
507 const std::vector<detail::RawActivity<T>>& acts = m.
acts;
514 l.
nacts = acts.size();
522 const std::size_t N = l.
nidx;
524 l.
names.assign(N + 1, {});
526 l.
type.assign(N + 1, LqnElement::HOST);
527 l.
parent.assign(N + 1, 0);
528 l.
sched.assign(NT + 1, SchedStrategy::NONE);
529 l.
mult.assign(NT + 1, 0.0);
531 l.
repl.assign(NT + 1, 1.0);
532 l.
prio.assign(NT + 1, 0);
539 l.
isref.assign(NT + 1,
false);
540 l.
iscache.assign(NT + 1,
false);
543 l.
nitems.assign(N + 1, 0);
559 l.
actpretype.assign(N + 1, PrecedenceType::NONE);
569 std::unordered_map<std::string, std::size_t> byhash;
571 for (std::size_t p = 0; p < l.
nhosts; ++p) {
572 const std::size_t idx = p + 1;
573 l.
sched[idx] = procs[p].sched;
574 l.
mult[idx] = procs[p].mult;
575 l.
repl[idx] = procs[p].repl;
576 l.
names[idx] = procs[p].name;
578 l.
type[idx] = LqnElement::HOST;
588 for (std::size_t t = 0; t < l.
ntasks; ++t) {
589 const std::size_t idx = l.
tshift + t + 1;
590 l.
sched[idx] = tasks[t].sched;
592 l.
think[idx] = tasks[t].thinktime;
593 l.
mult[idx] = tasks[t].mult;
594 l.
repl[idx] = tasks[t].repl;
595 l.
prio[idx] = tasks[t].priority;
601 l.
names[idx] = tasks[t].name;
604 l.
nitems[idx] = tasks[t].nitems;
605 l.
itemcap[idx] = tasks[t].itemcap;
607 l.
iscache[idx] = tasks[t].nitems > 0;
613 l.
hassetup[idx] = !tasks[t].setuptime.disabled &&
618 : tasks[t].sched == SchedStrategy::REF ?
"R:"
621 l.
parent[idx] = tasks[t].proc_slot + 1;
623 l.
type[idx] = LqnElement::TASK;
627 for (std::size_t t = 0; t < l.
ntasks; ++t) {
628 const std::size_t tidx = l.
tshift + t + 1;
636 std::unordered_map<std::string, std::size_t> task_by_name;
637 for (std::size_t t = 0; t < l.
ntasks; ++t) {
638 const std::size_t tidx = l.
tshift + t + 1;
639 task_by_name[l.
names[tidx]] = tidx;
641 for (std::size_t t = 0; t < l.
ntasks; ++t) {
642 const std::size_t tidx = l.
tshift + t + 1;
643 for (std::size_t k = 0; k < tasks[t].fanout.size(); ++k) {
644 const std::unordered_map<std::string, std::size_t>::const_iterator it =
645 task_by_name.find(tasks[t].fanout[k].first);
646 if (it != task_by_name.end())
647 l.
fanout[std::make_pair(tidx, it->second)] = tasks[t].fanout[k].second;
649 for (std::size_t k = 0; k < tasks[t].fanin.size(); ++k) {
650 const std::unordered_map<std::string, std::size_t>::const_iterator it =
651 task_by_name.find(tasks[t].fanin[k].first);
652 if (it != task_by_name.end())
653 l.
fanin[std::make_pair(tidx, it->second)] = tasks[t].fanin[k].second;
657 for (std::size_t p = 1; p <= l.
nhosts; ++p)
658 for (std::size_t idx = 1; idx <= NT; ++idx)
659 if (l.
type[idx] == LqnElement::TASK && l.
parent[idx] == p) l.
tasksof[p].push_back(idx);
661 for (std::size_t e = 0; e < l.
nentries; ++e) {
662 const std::size_t idx = l.
eshift + e + 1;
663 l.
names[idx] = entries[e].name;
665 l.
nitems[idx] = entries[e].cardinality;
666 l.
itemproc[idx] = entries[e].popularity;
667 l.
hashnames[idx] = (entries[e].cardinality > 0 ?
"I:" :
"E:") + entries[e].name;
670 l.
arrival[idx] = entries[e].arrival;
671 const std::size_t tidx = l.
tshift + entries[e].task_slot + 1;
673 l.
graph.set(tidx, idx, one);
675 l.
type[idx] = LqnElement::ENTRY;
678 for (std::size_t a = 0; a < l.
nacts; ++a) {
679 const std::size_t idx = l.
ashift + a + 1;
680 l.
names[idx] = acts[a].name;
685 const double speed = procs[tasks[acts[a].task_slot].proc_slot].speed_factor;
686 l.
hostdem[idx] = (speed != 1.0 && !acts[a].hostdem.is_immediate())
689 l.
actthink[idx] = acts[a].thinktime;
690 const std::size_t tidx = l.
tshift + acts[a].task_slot + 1;
692 l.
actsof[tidx].push_back(idx);
693 l.
type[idx] = LqnElement::ACTIVITY;
698 auto find_entry = [&](
const std::string& name) -> std::size_t {
699 auto it = byhash.find(
"E:" + name);
700 if (it != byhash.end())
return it->second;
701 it = byhash.find(
"I:" + name);
702 return it == byhash.end() ? 0 : it->second;
704 auto find_act = [&](
const std::string& name) -> std::size_t {
705 auto it = byhash.find(
"A:" + name);
706 return it == byhash.end() ? 0 : it->second;
710 std::vector<std::pair<std::size_t, std::size_t>> loop_back_edges;
711 std::unordered_map<std::string, std::string> bound_entry_to_act;
712 std::size_t cidx = 0;
714 auto add_call = [&](std::size_t src, std::size_t dst_e,
CallType ct,
const T& mean,
715 const std::string& arrow) {
727 l.
calltype.push_back(CallType::NONE);
732 for (std::size_t t = 0; t < l.
ntasks; ++t) {
733 const std::size_t tidx = l.
tshift + t + 1;
734 for (std::size_t a = 0; a < l.
nacts; ++a) {
735 if (acts[a].task_slot != t)
continue;
736 const std::size_t aidx = l.
ashift + a + 1;
738 if (!acts[a].bound_to_entry.empty()) {
739 const std::size_t eidx = find_entry(acts[a].bound_to_entry);
741 l.
graph.set(eidx, aidx, one);
742 auto it = bound_entry_to_act.find(acts[a].bound_to_entry);
743 if (it != bound_entry_to_act.end())
744 throw InputError(
ErrorKind{
"Format"},
"lqn reader: activities '" + it->second +
"' and '" +
745 acts[a].name +
"' are both bound to entry '" +
746 acts[a].bound_to_entry +
"'");
747 bound_entry_to_act[acts[a].bound_to_entry] = acts[a].name;
751 for (
const auto& c : acts[a].sync_calls) {
752 const std::size_t te = find_entry(c.dest);
755 "' calls unknown entry '" + c.dest +
"'");
756 const std::size_t tt = l.
parent[te];
758 throw InputError(
ErrorKind{
"Format"},
"lqn reader: an entry on a task cannot call another entry on "
759 "the same task ('" + acts[a].name +
"' -> '" + c.dest +
"')");
760 add_call(aidx, te, CallType::SYNC, c.mean,
"=>");
761 l.
callsof[aidx].push_back(cidx);
771 l.
graph.set(aidx, te, one);
773 for (
const auto& c : acts[a].async_calls) {
774 const std::size_t te = find_entry(c.dest);
777 "' has an async call to unknown entry '" + c.dest +
"'");
778 const std::size_t tt = l.
parent[te];
780 throw InputError(
ErrorKind{
"Format"},
"lqn reader: async self-call from '" + acts[a].name +
"'");
781 add_call(aidx, te, CallType::ASYNC, c.mean,
"->");
782 l.
callsof[aidx].push_back(cidx);
792 l.
graph.set(aidx, te, one);
798 for (
const auto& g : acts[a].call_groups) {
802 for (
const std::string& nm : g.second) {
803 const std::size_t te = find_entry(nm);
806 "' dispatches a call group to unknown entry '" + nm +
814 for (
const auto& pr : tasks[t].precedences) {
827 if (pr.pretype == PrecedenceType::PRE_AND && pr.has_quorum &&
830 bool resolved =
true;
831 for (
const std::string& nm : pr.preacts) {
832 const std::size_t ai = find_act(nm);
833 if (ai == 0) { resolved =
false;
break; }
836 for (
const std::string& nm : pr.postacts) {
837 const std::size_t ai = find_act(nm);
838 if (ai == 0) { resolved =
false;
break; }
843 std::size_t quorum_count = 0;
844 if (pr.pretype == PrecedenceType::PRE_AND) {
845 if (pr.preacts.empty())
846 throw InputError(
ErrorKind{
"Format"},
"lqn reader: PRE_AND precedence with no pre activities in "
847 "task '" + tasks[t].name +
"'");
848 quorum_count = (pr.has_quorum && pr.quorum >= 1 && pr.quorum <= pr.preacts.size())
852 for (std::size_t pa = 0; pa < pr.preacts.size(); ++pa) {
853 const std::size_t preaidx = find_act(pr.preacts[pa]);
856 pr.preacts[pa] +
"'");
857 switch (pr.posttype) {
858 case PrecedenceType::POST_OR:
859 for (std::size_t po = 0; po < pr.postacts.size(); ++po) {
860 const std::size_t postaidx = find_act(pr.postacts[po]);
861 l.
graph.set(preaidx, postaidx, pr.postparams[po]);
866 case PrecedenceType::POST_AND:
867 for (std::size_t po = 0; po < pr.postacts.size(); ++po) {
868 const std::size_t postaidx = find_act(pr.postacts[po]);
869 l.
graph.set(preaidx, postaidx, one);
874 case PrecedenceType::POST_LOOP: {
876 const T counts = pr.postparams.empty() ? one : pr.postparams[0];
877 const std::size_t enda = pr.postacts.size() - 1;
878 const std::size_t loopentry = find_act(pr.preacts[0]);
879 const std::size_t loopstart = find_act(pr.postacts[0]);
880 const std::size_t loopend = find_act(pr.postacts[enda]);
882 l.
graph.set(loopentry, loopstart, counts);
883 l.
graph.set(loopentry, loopend, T(one - counts));
884 std::size_t cur = loopstart;
885 for (std::size_t po = 1; po + 1 < pr.postacts.size(); ++po) {
886 const std::size_t pi = find_act(pr.postacts[po]);
887 l.
graph.set(cur, pi, one);
891 l.
graph.set(cur, loopend, one);
894 std::size_t cur = loopentry;
895 for (std::size_t po = 0; po + 1 < pr.postacts.size(); ++po) {
896 const std::size_t pi = find_act(pr.postacts[po]);
897 l.
graph.set(cur, pi, one);
901 loop_back_edges.emplace_back(cur, loopstart);
902 l.
graph.set(cur, loopstart, T(one - one / counts));
903 l.
graph.set(cur, loopend, T(one / counts));
909 for (std::size_t po = 0; po < pr.postacts.size(); ++po) {
910 const std::size_t postaidx = find_act(pr.postacts[po]);
913 pr.postacts[po] +
"'");
914 l.
graph.set(preaidx, postaidx, one);
917 if (quorum_count > 0) l.
actquorum[postaidx] = quorum_count;
926 for (std::size_t e = 0; e < l.
nentries; ++e) {
927 const std::size_t eidx = l.
eshift + e + 1;
928 const std::size_t src_t = l.
parent[eidx];
929 for (std::size_t f = 0; f < entries[e].fwd_dest.size(); ++f) {
930 const std::size_t te = find_entry(entries[e].fwd_dest[f]);
933 "' forwards to unknown entry '" + entries[e].fwd_dest[f] +
"'");
934 if (l.
parent[te] == src_t)
936 "' forwards to an entry on the same task");
937 add_call(eidx, te, CallType::FWD, entries[e].fwd_prob[f],
"~>");
939 l.
graph.set(eidx, te, one);
949 for (std::size_t cidx2 = 1; cidx2 <= l.
tshift + l.
ntasks; ++cidx2) {
950 const bool ishost = cidx2 <= l.
nhosts;
951 const std::vector<std::size_t>& colIdx =
953 const char* colwhat = ishost ?
"tasks on this host" :
"entries of this task";
955 const std::vector<T>* rawb =
nullptr;
956 const std::vector<detail::RawLinConRow<T>>* rows =
nullptr;
958 typename std::map<std::size_t, std::vector<detail::RawLinConRow<T>>>::const_iterator
961 typename std::map<std::size_t,
962 std::pair<Matrix<T>, std::vector<T>>>::const_iterator ip =
965 rawA = &ip->second.first;
966 rawb = &ip->second.second;
969 const detail::RawTask<T>& rt = tasks[cidx2 - l.
tshift - 1];
970 rows = &rt.linconrows;
974 const std::size_t ncols = colIdx.size();
975 std::vector<std::vector<T>> Arows;
976 std::vector<T> brows;
977 if (rawA !=
nullptr && rawA->
rows() > 0) {
978 if (rawA->
cols() != ncols)
980 "' has " + std::to_string(rawA->
cols()) +
" columns but there are " +
981 std::to_string(ncols) +
" " + colwhat);
982 for (std::size_t k = 0; k < rawA->
rows(); ++k) {
983 std::vector<T> row(ncols, zero);
984 for (std::size_t j = 0; j < ncols; ++j) row[j] = (*rawA)(k, j);
985 Arows.push_back(row);
986 brows.push_back(k < rawb->size() ? (*rawb)[k] : zero);
989 if (rows !=
nullptr) {
990 for (std::size_t r = 0; r < rows->size(); ++r) {
991 std::vector<T> row(ncols, zero);
992 for (std::size_t k = 0; k < (*rows)[r].names.size(); ++k) {
993 std::size_t pos = ncols;
994 for (std::size_t j = 0; j < ncols; ++j)
995 if (l.
names[colIdx[j]] == (*rows)[r].names[k]) pos = j;
998 "' names '" + (*rows)[r].names[k] +
999 "', which is not one of the " + colwhat);
1000 row[pos] = T(row[pos] + (*rows)[r].coeffs[k]);
1002 Arows.push_back(row);
1003 brows.push_back((*rows)[r].cap);
1006 if (Arows.empty())
continue;
1008 for (std::size_t k = 0; k < Arows.size(); ++k)
1009 for (std::size_t j = 0; j < ncols; ++j) A(k, j) = Arows[k][j];
1018 for (std::size_t pidx = 1; pidx <= l.
tshift + l.
ntasks; ++pidx) {
1019 const bool ishost = pidx <= l.
nhosts;
1020 const std::vector<std::size_t>& colIdx =
1022 const char* colwhat = ishost ?
"tasks on this host" :
"entries of this task";
1023 const std::vector<detail::RawServerPool<T>>* raw =
nullptr;
1025 typename std::map<std::size_t,
1026 std::vector<detail::RawServerPool<T>>>::const_iterator it =
1028 if (it != m.
proc_pools.end()) raw = &it->second;
1030 raw = &tasks[pidx - l.
tshift - 1].pools;
1032 if (raw ==
nullptr || raw->empty())
continue;
1033 const std::size_t ncols = colIdx.size();
1036 "' but the element has no operand to serve");
1039 for (std::size_t t2 = 0; t2 < raw->size(); ++t2) {
1040 const detail::RawServerPool<T>& rp = (*raw)[t2];
1041 sp.names.push_back(rp.name);
1042 sp.counts.push_back(rp.count);
1043 sp.rates.push_back(rp.rate);
1044 for (std::size_t k = 0; k < rp.compatible.size(); ++k) {
1046 for (std::size_t j = 0; j < ncols; ++j) {
1047 if (l.
names[colIdx[j]] == rp.compatible[k]) {
1048 sp.compat(t2, j) = one;
1055 l.
names[pidx] +
"' names '" + rp.compatible[k] +
1056 "', which is not one of the " + colwhat);
1061 for (std::size_t j = 0; j < ncols; ++j) {
1062 bool served =
false;
1063 for (std::size_t t2 = 0; t2 < sp.npools(); ++t2)
1064 if (sp.compat(t2, j) != zero) served =
true;
1067 "' is compatible with no server pool, so it can never be served");
1073 for (std::size_t e = 1; e <= l.
nentries; ++e) {
1074 const std::size_t eidx = l.
eshift + e;
1076 for (std::size_t s : l.
graph.succ(eidx))
1077 if (s > l.
ashift) bound =
true;
1080 if (!bound)
throw InputError(
ErrorKind{
"Format"},
"An entry does not have any boundTo activity.");
1087 std::vector<char> reached(l.
nidx + 1, 0);
1088 std::vector<std::size_t> stack;
1089 for (std::size_t e = 1; e <= l.
nentries; ++e) {
1090 reached[l.
eshift + e] = 1;
1091 stack.push_back(l.
eshift + e);
1093 while (!stack.empty()) {
1094 const std::size_t v = stack.back();
1096 for (std::size_t w : l.
graph.succ(v)) {
1097 if (w <= l.ashift || w > l.
nidx || reached[w])
continue;
1102 for (std::size_t a = 1; a <= l.
nacts; ++a)
1103 if (!reached[l.
ashift + a])
1105 "' is not reachable from any entry of its task: bind it with boundTo or link it "
1106 "with a precedence.");
1115 for (std::size_t e = 0; e < entries.size(); ++e) {
1116 for (
const std::string& rname : entries[e].reply_activities) {
1117 const std::size_t aidx = find_act(rname);
1118 if (aidx == 0)
continue;
1119 for (std::size_t succ : l.
graph.succ(aidx)) {
1120 if (succ <= l.
ashift)
continue;
1128 for (std::size_t tidx = 1; tidx <= NT; ++tidx) {
1129 if (l.
sched[tidx] != SchedStrategy::INF)
continue;
1130 if (l.
type[tidx] != LqnElement::TASK)
continue;
1132 for (std::size_t c = 1; c <= NT; ++c)
1137 for (std::size_t idx = 1; idx <= NT; ++idx) l.
isref[idx] = l.
sched[idx] == SchedStrategy::REF;
1141 for (std::size_t i = 1; i <= N; ++i) {
1142 if (l.
type[i] != LqnElement::TASK || l.
isref[i])
continue;
1143 std::vector<std::size_t> to_flip;
1144 for (
const auto& e : l.
dag.row[i])
1145 if (l.
type[e.first] == LqnElement::ENTRY && e.second != zero)
1146 to_flip.push_back(e.first);
1147 for (std::size_t j : to_flip) {
1149 l.
dag.set(j, i, one);
1152 for (
const auto& be : loop_back_edges) l.
dag.erase(be.first, be.second);
1155 for (std::size_t e = 1; e <= l.
nentries; ++e) {
1156 const std::size_t eidx = l.
eshift + e;
1157 const std::size_t tidx = l.
parent[eidx];
1158 std::vector<bool> visited(N + 1,
false);
1159 std::vector<std::size_t> stack{eidx};
1160 visited[eidx] =
true;
1161 while (!stack.empty()) {
1162 const std::size_t v = stack.back();
1164 for (std::size_t w : l.
graph.succ(v))
1170 std::vector<std::size_t> found;
1171 for (std::size_t i = 1; i <= N; ++i)
1172 if (visited[i] && l.
type[i] == LqnElement::ACTIVITY && l.
parent[i] == tidx)
1181 for (std::size_t e = 1; e <= l.
nentries; ++e) {
1182 const std::size_t eidx = l.
eshift + e;
1183 bool sync =
false, async =
false;
1184 for (std::size_t c = 1; c <= l.
ncalls; ++c) {
1186 if (l.
calltype[c] == CallType::SYNC) sync =
true;
1187 if (l.
calltype[c] == CallType::ASYNC) async =
true;
1191 "' is called both synchronously and asynchronously");
1214inline std::string fmt_num(
double v) {
1216 std::snprintf(buf,
sizeof(buf),
"%g", v);
1217 return std::string(buf);
1221inline double attr_num(
const std::string& s,
double dflt) {
1222 if (s.empty())
return dflt;
1224 return std::stod(s);
1225 }
catch (
const std::exception&) {
1226 return std::numeric_limits<double>::quiet_NaN();
1231inline bool iequals(
const std::string& a,
const std::string& b) {
1232 if (a.size() != b.size())
return false;
1233 for (std::size_t i = 0; i < a.size(); ++i)
1234 if (std::tolower(
static_cast<unsigned char>(a[i])) !=
1235 std::tolower(
static_cast<unsigned char>(b[i])))
1241inline std::string call_order_from_text(
const std::string& s) {
1242 if (iequals(s,
"DETERMINISTIC"))
return "DETERMINISTIC";
1243 return "STOCHASTIC";
1256inline void validate_input_model(
const xml::Element& doc) {
1257 const double tol = 1e-6;
1258 std::vector<std::string> proc_names;
1259 std::vector<std::string> task_names;
1260 std::vector<std::string> entry_names;
1261 std::vector<std::string> entry_owner;
1262 std::vector<bool> is_ref_entry;
1263 std::vector<std::string> call_dests;
1264 std::vector<std::string> reply_entries;
1265 bool has_ref_task =
false;
1266 bool has_open_arrival =
false;
1268 for (
const xml::Element* pe : doc.by_tag(
"processor")) {
1269 const std::string proc_name = pe->attr(
"name");
1270 if (std::find(proc_names.begin(), proc_names.end(), proc_name) != proc_names.end())
1271 throw InputError(ErrorKind{
"Format"},
"Duplicate processor name \"" + proc_name +
"\".");
1272 proc_names.push_back(proc_name);
1274 for (
const xml::Element* te : pe->by_tag(
"task")) {
1275 const std::string task_name = te->attr(
"name");
1276 if (std::find(task_names.begin(), task_names.end(), task_name) != task_names.end())
1277 throw InputError(ErrorKind{
"Format"},
"Duplicate task name \"" + task_name +
"\".");
1278 task_names.push_back(task_name);
1279 const bool is_ref = iequals(te->attr(
"scheduling"),
"ref");
1280 has_ref_task = has_ref_task || is_ref;
1282 const std::vector<const xml::Element*> entry_els = te->by_tag(
"entry");
1283 if (entry_els.empty())
1284 throw InputError(ErrorKind{
"Format"},
"Task \"" + task_name +
"\" has no entries.");
1285 for (
const xml::Element* ee : entry_els) {
1286 const std::string entry_name = ee->attr(
"name");
1287 if (std::find(entry_names.begin(), entry_names.end(), entry_name) !=
1289 throw InputError(ErrorKind{
"Format"},
"Duplicate entry name \"" + entry_name +
"\".");
1290 entry_names.push_back(entry_name);
1291 entry_owner.push_back(task_name);
1292 is_ref_entry.push_back(is_ref);
1294 const double arrival_rate =
1295 attr_num(ee->attr(
"open-arrival-rate"), std::numeric_limits<double>::quiet_NaN());
1296 if (arrival_rate > 0.0) {
1297 has_open_arrival =
true;
1299 throw InputError(ErrorKind{
"Format"},
"Entry \"" + entry_name +
"\" belongs to reference task \"" +
1300 task_name +
"\" and cannot have open arrivals.");
1303 const std::vector<const xml::Element*> fwd_els = ee->by_tag(
"forwarding");
1304 if (is_ref && !fwd_els.empty())
1305 throw InputError(ErrorKind{
"Format"},
"Entry \"" + entry_name +
"\" belongs to reference task \"" +
1306 task_name +
"\" and cannot forward requests.");
1307 double fwd_total = 0.0;
1308 for (
const xml::Element* fe : fwd_els) {
1309 const double prob = attr_num(fe->attr(
"prob"), 1.0);
1310 if (std::isnan(prob) || prob < 0.0 || prob > 1.0)
1311 throw InputError(ErrorKind{
"Format"},
"Forwarding from entry \"" + entry_name +
"\" to entry \"" +
1312 fe->attr(
"dest") +
"\" has an invalid probability of " +
1313 fmt_num(prob) +
".");
1316 if (fwd_total > 1.0 + tol)
1317 throw InputError(ErrorKind{
"Format"},
"Entry \"" + entry_name +
1318 "\" has a total forwarding probability of " +
1319 fmt_num(fwd_total) +
".");
1323 std::vector<std::string> act_names;
1324 for (
const xml::Element* ae : te->by_tag(
"activity")) {
1325 if (ae->parent ==
nullptr)
continue;
1326 if (ae->parent->name !=
"task-activities" &&
1327 ae->parent->name !=
"entry-phase-activities")
1329 const std::string act_name = ae->attr(
"name");
1330 if (std::find(act_names.begin(), act_names.end(), act_name) != act_names.end())
1331 throw InputError(ErrorKind{
"Format"},
"Duplicate activity name \"" + act_name +
"\" in task \"" +
1333 act_names.push_back(act_name);
1336 for (
const xml::Element* ce : te->by_tag(
"synch-call"))
1337 call_dests.push_back(ce->attr(
"dest"));
1338 for (
const xml::Element* ce : te->by_tag(
"asynch-call"))
1339 call_dests.push_back(ce->attr(
"dest"));
1340 for (
const xml::Element* fe : te->by_tag(
"forwarding"))
1341 call_dests.push_back(fe->attr(
"dest"));
1343 for (
const xml::Element* oe : te->by_tag(
"post-OR")) {
1344 double branch_total = 0.0;
1345 for (
const xml::Element* be : oe->by_tag(
"activity")) {
1346 const double prob = attr_num(be->attr(
"prob"), 1.0);
1347 if (std::isnan(prob) || prob < 0.0 || prob > 1.0)
1348 throw InputError(ErrorKind{
"Format"},
"Activity \"" + be->attr(
"name") +
"\" in task \"" +
1349 task_name +
"\" has an invalid branch probability of " +
1350 fmt_num(prob) +
".");
1351 branch_total += prob;
1353 if (std::fabs(branch_total - 1.0) > tol)
1354 throw InputError(ErrorKind{
"Format"},
"Branch probabilities of an OR-fork in task \"" + task_name +
1355 "\" sum to " + fmt_num(branch_total) +
" instead of 1.");
1358 for (
const xml::Element* re : te->by_tag(
"reply-entry"))
1359 reply_entries.push_back(
re->attr(
"name"));
1363 for (
const std::string& dest : call_dests) {
1364 const std::vector<std::string>::const_iterator it =
1365 std::find(entry_names.begin(), entry_names.end(), dest);
1366 if (it == entry_names.end())
continue;
1367 const std::size_t idx =
static_cast<std::size_t
>(it - entry_names.begin());
1368 if (is_ref_entry[idx])
1369 throw InputError(ErrorKind{
"Format"},
"Entry \"" + entry_names[idx] +
"\" belongs to reference task \"" +
1370 entry_owner[idx] +
"\" and cannot receive requests.");
1373 for (
const std::string& reply_name : reply_entries) {
1374 const std::vector<std::string>::const_iterator it =
1375 std::find(entry_names.begin(), entry_names.end(), reply_name);
1376 if (it == entry_names.end())
continue;
1377 const std::size_t idx =
static_cast<std::size_t
>(it - entry_names.begin());
1378 if (is_ref_entry[idx])
1379 throw InputError(ErrorKind{
"Format"},
"Entry \"" + entry_names[idx] +
"\" belongs to reference task \"" +
1380 entry_owner[idx] +
"\" and cannot be replied to.");
1383 if (!has_ref_task && !has_open_arrival)
1384 throw InputError(ErrorKind{
"Format"},
"The model has no reference task and no open arrivals.");
1393 std::vector<detail::RawProc>& procs = m.
procs;
1394 std::vector<detail::RawTask<T>>& tasks = m.
tasks;
1395 std::vector<detail::RawEntry<T>>& entries = m.
entries;
1396 std::vector<detail::RawActivity<T>>& acts = m.
acts;
1398 detail::validate_input_model(*doc);
1402 const std::vector<const xml::Element*> proc_els = doc->by_tag(
"processor");
1405 pr.name = pe->attr(
"name");
1406 const std::string psched = pe->attr(
"scheduling");
1411 if (pr.sched == SchedStrategy::INF) {
1414 pr.mult = std::numeric_limits<double>::infinity();
1418 const std::size_t proc_slot = procs.size();
1419 procs.push_back(pr);
1422 detail::RawTask<T> tk;
1423 tk.name = te->attr(
"name");
1424 const std::string tsched = te->attr(
"scheduling");
1429 if (!std::isnan(prio_d)) tk.priority =
static_cast<int>(prio_d);
1430 if (tk.sched == SchedStrategy::INF) {
1431 tk.mult = std::numeric_limits<double>::infinity();
1435 const std::string think_s = te->attr(
"think-time");
1439 tk.proc_slot = proc_slot;
1444 tk.fanout.push_back(
1445 std::make_pair(fe->attr(
"dest"),
dbl_from_decimal(fe->attr(
"value"), 1.0)));
1448 std::make_pair(fe->attr(
"source"),
dbl_from_decimal(fe->attr(
"value"), 1.0)));
1457 tk.setuptime = detail::setup_time<T>(se->attr(
"mean"), se->attr(
"scv"));
1459 tk.delayofftime = detail::setup_time<T>(se->attr(
"mean"), se->attr(
"scv"));
1469 const std::string items_s = ce->attr(
"items");
1471 tk.nitems = items_d > 0.0 ?
static_cast<std::size_t
>(items_d) : 1;
1472 tk.replacestrat = detail::replacement_from_lqnx(ce->attr(
"replacement"));
1474 const std::string rs = ce->attr(
"retrieval");
1475 tk.retrieval = detail::iequals(rs,
"true") || rs ==
"1";
1479 tk.itemcap.push_back(cap_d > 0.0 ?
static_cast<int>(cap_d) : 1);
1484 if (tk.itemcap.empty()) tk.itemcap.push_back(1);
1486 const std::size_t task_slot = tasks.size();
1487 tasks.push_back(tk);
1490 detail::RawEntry<T> en;
1491 en.name = ee->attr(
"name");
1492 en.task_slot = task_slot;
1493 const std::string arr_s = ee->attr(
"open-arrival-rate");
1494 if (!arr_s.empty()) {
1497 en.has_arrival =
true;
1502 en.fwd_dest.push_back(fe->attr(
"dest"));
1503 const std::string ps = fe->attr(
"prob");
1512 en.cardinality = card_d > 0.0 ?
static_cast<std::size_t
>(card_d) : 1;
1513 en.popularity = detail::popularity_from_lqnx<T>(ie2, en.cardinality);
1515 const std::size_t entry_slot = entries.size();
1516 entries.push_back(en);
1518 const std::vector<const xml::Element*> epa = ee->by_tag(
"entry-phase-activities");
1521 std::map<int, std::string> by_phase;
1522 for (
const xml::Element* ae : epa[0]->by_tag(
"activity")) {
1523 const int phase =
static_cast<int>(
dbl_from_decimal(ae->attr(
"phase"), 1.0));
1524 detail::RawActivity<T> ac;
1525 ac.name = ae->attr(
"name");
1526 ac.hostdem = detail::host_demand<T>(ae->attr(
"host-demand-mean"),
1527 ae->attr(
"host-demand-cvsq"));
1528 const std::string att = ae->attr(
"think-time");
1530 ac.thinktime = att_d > 0.0
1533 ac.bound_to_entry = phase == 1 ? entries[entry_slot].name : std::string();
1535 ac.call_order = detail::call_order_from_text(ae->attr(
"call-order"));
1536 ac.task_slot = task_slot;
1538 ac.sync_calls.push_back(
1541 ac.async_calls.push_back(
1543 detail::read_call_groups<T>(ae, ac);
1544 by_phase[phase] = ac.name;
1551 if (!by_phase.empty() && !by_phase.count(1)) {
1552 const std::string& en = entries[entry_slot].name;
1553 std::vector<std::string> taken;
1554 for (
const xml::Element* de : doc->by_tag(
"activity")) taken.push_back(de->attr(
"name"));
1555 std::string ph1 = en +
"_ph1";
1557 while (std::find(taken.begin(), taken.end(), ph1) != taken.end())
1558 ph1 = en +
"_ph1_" + std::to_string(++suffix);
1560 std::fprintf(stderr,
"[LINE] Warning: parseXML: Entry %s declares no phase-1 activity and %s_ph1 is "
1561 "already an activity; its synthetic phase-1 activity is named %s.\n",
1562 en.c_str(), en.c_str(), ph1.c_str());
1563 detail::RawActivity<T> ac;
1567 ac.bound_to_entry = entries[entry_slot].name;
1569 ac.task_slot = task_slot;
1570 by_phase[1] = ac.name;
1574 for (
auto it = by_phase.begin(); it != by_phase.end(); ++it) {
1575 auto nx = std::next(it);
1576 if (nx == by_phase.end())
break;
1577 detail::RawPrecedence<T> pr;
1578 pr.pretype = PrecedenceType::PRE_SEQ;
1579 pr.posttype = PrecedenceType::POST_SEQ;
1580 pr.preacts.push_back(it->second);
1581 pr.postacts.push_back(nx->second);
1582 tasks[task_slot].precedences.push_back(pr);
1584 if (!by_phase.empty() && by_phase.count(1))
1585 entries[entry_slot].reply_activities.push_back(by_phase[1]);
1589 const std::vector<const xml::Element*> tal = te->by_tag(
"task-activities");
1594 if (ae->parent != ta)
continue;
1595 detail::RawActivity<T> ac;
1596 ac.name = ae->attr(
"name");
1597 ac.hostdem = detail::host_demand<T>(ae->attr(
"host-demand-mean"),
1598 ae->attr(
"host-demand-cvsq"));
1599 const std::string att = ae->attr(
"think-time");
1603 ac.bound_to_entry = ae->attr(
"bound-to-entry");
1605 ac.call_order = detail::call_order_from_text(ae->attr(
"call-order"));
1606 ac.task_slot = task_slot;
1608 ac.sync_calls.push_back(
1611 ac.async_calls.push_back(
1613 detail::read_call_groups<T>(ae, ac);
1618 detail::RawPrecedence<T> pr;
1620 if (!pe2->child_tags(
"pre").empty()) {
1622 pr.pretype = PrecedenceType::PRE_SEQ;
1623 }
else if (!pe2->child_tags(
"pre-AND").empty()) {
1625 pr.pretype = PrecedenceType::PRE_AND;
1626 }
else if (!pe2->child_tags(
"pre-OR").empty()) {
1628 pr.pretype = PrecedenceType::PRE_OR;
1633 pr.preacts.push_back(ae->attr(
"name"));
1634 if (pr.pretype == PrecedenceType::PRE_OR)
1637 if (pr.pretype == PrecedenceType::PRE_SEQ && pr.preacts.size() > 1)
1638 pr.preacts.resize(1);
1639 if (pr.pretype == PrecedenceType::PRE_AND) {
1640 const std::string q = pre->
attr(
"quorum");
1642 pr.has_quorum =
true;
1648 if (!pe2->child_tags(
"post").empty()) {
1650 pr.posttype = PrecedenceType::POST_SEQ;
1651 }
else if (!pe2->child_tags(
"post-AND").empty()) {
1653 pr.posttype = PrecedenceType::POST_AND;
1654 }
else if (!pe2->child_tags(
"post-OR").empty()) {
1656 pr.posttype = PrecedenceType::POST_OR;
1657 }
else if (!pe2->child_tags(
"post-LOOP").empty()) {
1659 pr.posttype = PrecedenceType::POST_LOOP;
1660 }
else if (!pe2->child_tags(
"post-CACHE").empty()) {
1662 pr.posttype = PrecedenceType::POST_CACHE;
1671 pr.postacts.push_back(ae->attr(
"name"));
1672 if (pr.posttype == PrecedenceType::POST_OR)
1674 if (pr.posttype == PrecedenceType::POST_LOOP)
1677 if (pr.posttype == PrecedenceType::POST_CACHE) {
1685 std::vector<std::string> hit, miss, unlabelled;
1686 const std::vector<const xml::Element*> aes = post->
by_tag(
"activity");
1687 for (std::size_t k = 0; k < aes.size(); ++k) {
1688 const std::string res = aes[k]->attr(
"cache-result");
1690 hit.push_back(pr.postacts[k]);
1691 else if (res ==
"miss")
1692 miss.push_back(pr.postacts[k]);
1694 unlabelled.push_back(pr.postacts[k]);
1696 if (!hit.empty() || !miss.empty()) {
1698 pr.postacts.insert(pr.postacts.end(), miss.begin(), miss.end());
1699 pr.postacts.insert(pr.postacts.end(), unlabelled.begin(),
1702 if (pr.postacts.size() < 2)
1704 "lqn reader: a <post-CACHE> branches on a hit and a miss, so it "
1705 "names two activities");
1707 if (pr.posttype == PrecedenceType::POST_LOOP)
1708 pr.postacts.push_back(post->
attr(
"end"));
1709 tasks[task_slot].precedences.push_back(pr);
1713 const std::string ename = re->attr(
"name");
1714 std::size_t slot = entries.size();
1715 for (std::size_t s = 0; s < entries.size(); ++s)
1716 if (entries[s].name == ename) {
1720 if (slot == entries.size())
1721 throw InputError(
ErrorKind{
"Format"},
"lqn reader: <reply-entry> names unknown entry '" + ename +
1723 for (
const xml::Element* ra : re->by_tag(
"reply-activity"))
1724 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,...
Rate-scaled copy of a distribution, preserving its shape.
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.
std::string attr(const char *name, const Val &v)
std::string name(const Element *el, const std::string &key="name")
LqnModel< T > read_lqnx_model(const std::string &path)
read_lqnx_model on a .lqnx file.
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.
LqnModel< T > read_lqnx_model_text(const std::string &text)
read_lqnx_model on a .lqnx document held in a string (the SRVN reader's translation,...
std::vector< double > lqn_sustainable_mult(const LqnStruct< T > &l, const std::vector< double > &mult)
Sustainable multiplicity (lsn_max_multiplicity) of every host and task of l under the declared multip...
LqnModel< T > read_lqnx_model_doc(std::unique_ptr< xml::Element > doc)
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(const std::string &text)
Parse a whole XML document and return its root element.
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.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
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 point mass at zero.
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
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 ....