481 const std::size_t nidx =
lsn.nidx;
482 if (nidx == 0)
throw InputError(
"SolverLDES (native LN engine): empty layered model");
487 for (std::size_t k = 1; k <= nidx && k <
lsn.sched.size(); ++k) {
488 const bool is_host = k >
lsn.hshift && k <=
lsn.hshift +
lsn.nhosts;
489 const bool is_task = k >
lsn.tshift && k <=
lsn.tshift +
lsn.ntasks;
490 if (!is_host && !is_task)
continue;
491 const SchedStrategy sc =
lsn.sched[k];
492 const bool ok = sc == SchedStrategy::FCFS || sc == SchedStrategy::INF ||
493 sc == SchedStrategy::LCFS || sc == SchedStrategy::SIRO ||
494 sc == SchedStrategy::HOL || sc == SchedStrategy::REF ||
495 sc == SchedStrategy::LCFSPRIO ||
496 (is_host && (sc == SchedStrategy::PS || sc == SchedStrategy::PSPRIO ||
497 sc == SchedStrategy::FCFSPRPRIO ||
498 sc == SchedStrategy::FCFSPIPRIO ||
499 sc == SchedStrategy::LCFSPRPRIO ||
500 sc == SchedStrategy::LCFSPIPRIO));
503 for (std::size_t c = 0; c < scname.size(); ++c)
504 scname[c] =
static_cast<char>(std::toupper(
static_cast<unsigned char>(scname[c])));
506 "SolverLDES (native LN engine) does not simulate " + scname +
" scheduling at " +
507 (is_host ?
"processor" :
"task") +
" '" +
lsn.names[k] +
508 "'. The layered engine implements " +
509 (is_host ?
"FCFS, LCFS, SIRO, HOL, FCFSPRIO, LCFSPRIO, FCFSPRPRIO, FCFSPIPRIO, "
510 "LCFSPRPRIO, LCFSPIPRIO, PS, PSPRIO and INF"
511 :
"FCFS, LCFS, SIRO, HOL, FCFSPRIO, LCFSPRIO and INF (a task holds threads, "
512 "it does not divide or preempt them)") +
513 "; a weighted discipline would additionally need the per-task shares the layered "
514 "struct does not carry.");
516 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
519 if (t <
lsn.hassetup.size() &&
lsn.hassetup[t]) {
520 const double m = (t <
lsn.mult.size()) ?
lsn.mult[t] : 1.0;
523 "' declares a setup time on an infinite-server task, which "
524 "holds no thread to power down; give it a finite "
529 if (t <
lsn.pools.size() &&
lsn.pools[t].npools() > 0)
531 "' declares heterogeneous server pools; LDES holds server "
532 "pools on processors only");
538 "SolverLDES (native LN engine) dispatches call groups round robin or JSQ only (activity '" +
541 const std::uint64_t max_events =
543 const std::uint64_t base =
544 (o.
seed >= 0) ?
static_cast<std::uint64_t
>(o.
seed) : std::random_device{}();
553 const long long ln_seed =
static_cast<long long>(base);
554 std::vector<Rng> g_host, g_think;
555 g_host.reserve(nidx + 1);
556 g_think.reserve(nidx + 1);
557 for (std::size_t k = 0; k <= nidx; ++k) {
558 g_host.push_back(
engine::ln_rng(ln_seed,
static_cast<long long>(k) * 10 + 1000));
559 g_think.push_back(
engine::ln_rng(ln_seed,
static_cast<long long>(k) * 10));
564 std::vector<Sampler> hostdem(nidx + 1), think(nidx + 1), actthink(nidx + 1);
565 std::vector<bool> has_hostdem(nidx + 1,
false), has_think(nidx + 1,
false),
566 has_actthink(nidx + 1,
false);
569 std::vector<Rng> g_actthink;
570 g_actthink.reserve(nidx + 1);
571 for (std::size_t k = 0; k <= nidx; ++k)
572 g_actthink.push_back(
engine::ln_rng(ln_seed,
static_cast<long long>(k) * 10 + 6000));
573 for (std::size_t a =
lsn.ashift + 1; a <=
lsn.ashift +
lsn.nacts && a <
lsn.actthink.size(); ++a) {
575 actthink[a] = Sampler(
lsn.actthink[a],
"the think time of activity '" +
lsn.names[a] +
"'");
576 has_actthink[a] =
true;
579 for (std::size_t k = 1; k <= nidx; ++k) {
580 if (k <
lsn.hostdem.size() && !
lsn.hostdem[k].disabled &&
582 hostdem[k] = Sampler(
lsn.hostdem[k],
"the host demand of '" +
lsn.names[k] +
"'");
583 has_hostdem[k] =
true;
585 if (k <
lsn.think.size() && !
lsn.think[k].disabled &&
587 think[k] = Sampler(
lsn.think[k],
"the think time of '" +
lsn.names[k] +
"'");
593 std::vector<std::size_t> host_servers(nidx + 1, 1), task_threads(nidx + 1, 1);
594 std::vector<SchedStrategy> host_sched(nidx + 1, SchedStrategy::FCFS);
595 std::vector<SchedStrategy> task_sched(nidx + 1, SchedStrategy::FCFS);
598 for (std::size_t h =
lsn.hshift + 1; h <=
lsn.hshift +
lsn.nhosts; ++h) {
599 const double m = (h <
lsn.mult.size()) ?
lsn.mult[h] : 1.0;
600 if (h <
lsn.sched.size()) host_sched[h] =
lsn.sched[h];
601 host_servers[h] = (std::isfinite(m) && host_sched[h] != SchedStrategy::INF)
602 ? std::max<std::size_t>(1,
static_cast<std::size_t
>(m + 0.5))
603 : std::numeric_limits<std::size_t>::max();
605 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
606 const double m = (t <
lsn.mult.size()) ?
lsn.mult[t] : 1.0;
607 if (t <
lsn.sched.size()) task_sched[t] =
lsn.sched[t];
608 task_threads[t] = (std::isfinite(m) && task_sched[t] != SchedStrategy::INF)
609 ?
static_cast<std::size_t
>(m + 0.5)
610 : std::numeric_limits<std::size_t>::max();
623 std::vector<std::size_t> repl(nidx + 1, 1);
624 for (std::size_t k = 1; k <
lsn.repl.size() && k <= nidx; ++k) {
625 const double r =
lsn.repl[k];
626 repl[k] = (r > 1.0) ?
static_cast<std::size_t
>(r + 0.5) : 1;
628 std::vector<std::size_t> host_slot0(nidx + 1, 0);
629 std::size_t nhost_slots = 0;
630 for (std::size_t h =
lsn.hshift + 1; h <=
lsn.hshift +
lsn.nhosts; ++h) {
631 host_slot0[h] = nhost_slots;
632 nhost_slots += repl[h];
634 if (nhost_slots == 0) nhost_slots = 1;
637 auto host_slot = [&](std::size_t host, std::size_t trep) {
638 return host_slot0[host] + (trep % repl[host]);
643 std::vector<std::size_t> task_slot0(nidx + 1, 0);
644 std::size_t ntask_slots = 0;
645 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
646 task_slot0[t] = ntask_slots;
647 ntask_slots += repl[t];
649 if (ntask_slots == 0) ntask_slots = 1;
650 auto task_slot = [&](std::size_t task, std::size_t trep) {
651 return task_slot0[task] + (trep % repl[task]);
663 auto callee_replica = [&](std::size_t caller_task, std::size_t crep,
664 std::size_t callee_task) -> std::size_t {
665 const std::size_t rb = repl[callee_task];
666 if (rb <= 1)
return 0;
667 std::size_t f =
static_cast<std::size_t
>(
lsn.fanout_at(caller_task, callee_task) + 0.5);
669 const std::size_t ra = repl[caller_task];
670 f = (rb > ra) ? std::max<std::size_t>(1, rb / ra) : 1;
672 f = std::min(std::max<std::size_t>(1, f), rb);
673 const std::size_t k =
674 (f > 1) ?
static_cast<std::size_t
>(uniform01(g_call) *
static_cast<double>(f)) : 0;
675 return ((crep * f) + std::min(k, f - 1)) % rb;
679 const std::size_t a_lo =
lsn.ashift + 1, a_hi =
lsn.ashift +
lsn.nacts;
680 auto is_act = [&](std::size_t k) {
return k >= a_lo && k <= a_hi; };
681 auto wgt = [&](std::size_t i, std::size_t j) {
684 auto pretype = [&](std::size_t a) {
687 auto posttype = [&](std::size_t a) {
690 auto phase_of = [&](std::size_t a) ->
int {
691 const std::size_t k = a -
lsn.ashift;
692 return (k <
lsn.actphase.size() &&
lsn.actphase[k] > 0) ?
lsn.actphase[k] : 1;
696 std::vector<std::vector<std::size_t> > succ_act(nidx + 1);
697 for (std::size_t a = a_lo; a <= a_hi; ++a)
698 for (std::size_t s :
lsn.graph.succ(a))
699 if (is_act(s)) succ_act[a].push_back(s);
707 std::vector<std::vector<double> > or_prob(nidx + 1);
708 for (std::size_t a = a_lo; a <= a_hi; ++a) {
709 const std::vector<std::size_t>& sc = succ_act[a];
710 if (sc.size() < 2)
continue;
713 for (std::size_t s : sc) {
714 const double w = wgt(a, s);
715 if (w != 1.0 && w > 0.0) prob =
true;
719 for (std::size_t s : sc) tot += wgt(a, s);
720 for (std::size_t s : sc) {
721 const double w = wgt(a, s);
722 or_prob[a].push_back((tot > 0.0 && tot != 1.0) ? w / tot : w);
727 std::vector<std::size_t> join_required(nidx + 1, 0);
728 for (std::size_t a = a_lo; a <= a_hi; ++a)
730 ++join_required[succ_act[a][0]];
738 std::vector<std::size_t> join_quorum(nidx + 1, 0);
739 std::vector<std::vector<std::size_t> > join_inputs(nidx + 1);
740 for (std::size_t a = a_lo; a <= a_hi; ++a)
742 join_inputs[succ_act[a][0]].push_back(a);
743 for (std::size_t j = a_lo; j <= a_hi; ++j) {
744 if (join_required[j] == 0)
continue;
745 const std::size_t q = j <
lsn.actquorum.size() ?
lsn.actquorum[j] : 0;
746 join_quorum[j] = (q >= 1 && q < join_required[j]) ? q : join_required[j];
755 std::vector<char> seen(nidx + 1, 0), root_input(nidx + 1, 0);
756 std::deque<std::size_t> todo;
757 for (std::size_t j = a_lo; j <= a_hi; ++j)
758 if (join_required[j] > 0) todo.push_back(j);
759 while (!todo.empty()) {
760 const std::size_t a = todo.front();
762 if (seen[a])
continue;
768 const std::vector<std::size_t>& sc = succ_act[a];
769 const bool choose = sc.size() == 1 || !or_prob[a].empty() ||
772 for (std::size_t s : sc) todo.push_back(s);
774 for (std::size_t j = a_lo; j <= a_hi; ++j) {
775 if (join_quorum[j] >= join_required[j])
continue;
776 for (std::size_t a : join_inputs[j])
779 "SolverLDES (native LN engine): the AND join into '" +
lsn.names[j] +
780 "' declares a quorum of " + std::to_string(join_quorum[j]) +
" of " +
781 std::to_string(join_required[j]) +
" inputs, but its input '" +
782 lsn.names[a] +
"' follows an earlier join without an AND fork in between, "
783 "so the main line of the invocation could arrive late; a quorum join must "
784 "be fed by forked branches only");
792 std::vector<std::size_t> bound(nidx + 1, 0);
793 for (std::size_t e =
lsn.eshift + 1; e <=
lsn.eshift +
lsn.nentries; ++e) {
794 const std::size_t t =
lsn.parent[e];
795 const std::vector<std::size_t>& acts =
796 (t <
lsn.actsof.size()) ?
lsn.actsof[t] : std::vector<std::size_t>();
797 for (std::size_t a : acts)
798 if (wgt(e, a) > 0.0) {
802 if (bound[e] != 0 || acts.empty())
continue;
803 for (std::size_t a : acts) {
806 bool haspred =
false;
807 for (std::size_t o2 : acts)
808 if (o2 != a && wgt(o2, a) > 0.0) {
817 if (bound[e] == 0) bound[e] = acts[0];
821 std::vector<LnCacheState> caches;
822 std::vector<std::size_t> cache_of_slot(ntask_slots, LN_NONE);
823 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
824 if (t >=
lsn.iscache.size() || !
lsn.iscache[t])
continue;
825 const std::size_t n = t <
lsn.nitems.size() ?
lsn.nitems[t] : 0;
826 if (n == 0)
continue;
827 std::vector<int> cap = (t <
lsn.itemcap.size()) ?
lsn.itemcap[t] : std::vector<int>();
828 if (cap.empty()) cap.assign(1, 1);
829 for (std::size_t m = 0; m < repl[t]; ++m) {
833 cs.rs = (t <
lsn.replacestrat.size()) ?
lsn.replacestrat[t]
835 cs.levels.assign(cap.size(), std::list<std::size_t>());
836 cs.retrieval = t <
lsn.hasretrieval.size() &&
lsn.hasretrieval[t];
838 cs.inflight.assign(n, 0);
839 cs.held.assign(n, {});
841 cache_of_slot[task_slot(t, m)] = caches.size();
842 caches.push_back(cs);
845 std::vector<LnCacheAccess> accesses;
846 std::vector<std::size_t> access_of_driver(nidx + 1, LN_NONE);
847 std::vector<std::size_t> branch_reply(nidx + 1, 0);
848 for (std::size_t e =
lsn.eshift + 1; e <=
lsn.eshift +
lsn.nentries; ++e) {
849 const std::size_t t =
lsn.parent[e];
850 if (t <= lsn.tshift || t >
lsn.tshift +
lsn.ntasks)
continue;
851 if (cache_of_slot[task_slot(t, 0)] == LN_NONE)
continue;
852 const std::size_t drv = bound[e];
853 const bool is_item = e <
lsn.itemproc.size() && !
lsn.itemproc[e].empty();
856 if (!is_item)
continue;
857 if (drv == 0 || succ_act[drv].size() < 2)
859 "' has no cache access with a hit and a miss branch");
861 for (std::size_t m = 0; m < repl[t]; ++m) ca.caches.push_back(cache_of_slot[task_slot(t, m)]);
862 ca.hit = succ_act[drv][0];
863 ca.miss = succ_act[drv][1];
865 std::size_t card = e <
lsn.nitems.size() ?
lsn.nitems[e] : 0;
866 if (card == 0) card = caches[ca.caches[0]].nitems;
868 for (std::size_t i = 0; i < card; ++i) {
869 const double v = (i <
lsn.itemproc[e].size())
871 if (std::isnan(v) || v < 0.0)
872 throw InputError(
"SolverLDES (native LN engine): ItemEntry '" +
lsn.names[e] +
873 "' has a popularity that is not a pmf");
875 ca.cdf.push_back(
sum);
878 throw InputError(
"SolverLDES (native LN engine): ItemEntry '" +
lsn.names[e] +
879 "' has a popularity of zero total mass");
880 for (
double& c : ca.cdf) c /=
sum;
881 access_of_driver[drv] = accesses.size();
882 branch_reply[ca.hit] = e;
883 branch_reply[ca.miss] = e;
884 accesses.push_back(ca);
888 for (std::size_t a = a_lo; a <= a_hi; ++a) {
889 if (access_of_driver[a] != LN_NONE)
continue;
890 for (std::size_t s : succ_act[a])
893 "' reads a cache, but no ItemEntry of a cache task with "
894 "a popularity is bound to it");
904 std::vector<std::vector<double> > A;
905 std::vector<double> b;
906 std::map<std::size_t, int> col;
908 std::vector<Lincon> lincon(nidx + 1);
909 std::vector<bool> has_lincon(nidx + 1,
false);
910 for (std::size_t k = 1; k <
lsn.lincon_A.size() && k <= nidx; ++k) {
912 if (A.
rows() == 0)
continue;
913 const bool host = k >
lsn.hshift && k <=
lsn.hshift +
lsn.nhosts;
914 const std::vector<std::size_t>* ops =
nullptr;
915 if (host && k <
lsn.tasksof.size()) ops = &
lsn.tasksof[k];
916 if (!host && k <
lsn.entriesof.size()) ops = &
lsn.entriesof[k];
917 const std::size_t ncols = ops ? ops->size() : 0;
918 if (A.
cols() != ncols)
919 throw InputError(
"SolverLDES (native LN engine): the admission constraint on '" +
920 lsn.names[k] +
"' has " + std::to_string(A.
cols()) +
921 " columns but the server has " + std::to_string(ncols) +
" operands");
922 if (ncols == 0)
continue;
924 lc.A.assign(A.
rows(), std::vector<double>(ncols, 0.0));
925 for (std::size_t r = 0; r < A.
rows(); ++r)
927 for (std::size_t r = 0; r < A.
rows(); ++r)
928 lc.b.push_back((k <
lsn.lincon_b.size() && r <
lsn.lincon_b[k].size())
930 : std::numeric_limits<double>::infinity());
931 for (std::size_t c = 0; c < ncols; ++c) lc.col[(*ops)[c]] =
static_cast<int>(c);
933 has_lincon[k] =
true;
935 auto lincon_col = [&](std::size_t elem, std::size_t operand) ->
int {
936 if (!has_lincon[elem])
return -1;
937 const std::map<std::size_t, int>::const_iterator it = lincon[elem].col.find(operand);
938 return it == lincon[elem].col.end() ? -1 : it->second;
941 auto admits = [&](std::size_t elem,
const std::vector<int>& occ,
int col) {
942 const Lincon& lc = lincon[elem];
943 for (std::size_t r = 0; r < lc.A.size(); ++r) {
944 double lhs = lc.A[r][
static_cast<std::size_t
>(col)];
945 for (std::size_t k = 0; k < occ.size(); ++k) lhs += lc.A[r][k] * occ[k];
946 if (lhs > lc.b[r] + 1e-9)
return false;
957 std::vector<bool> pooled(nidx + 1,
false);
958 std::vector<std::vector<double> > pool_capacity(nidx + 1);
959 std::vector<std::vector<std::vector<bool> > > pool_serves(nidx + 1);
960 std::vector<std::map<std::size_t, int> > pool_col(nidx + 1);
961 for (std::size_t h =
lsn.hshift + 1; h <=
lsn.hshift +
lsn.nhosts; ++h) {
962 if (h >=
lsn.pools.size() ||
lsn.pools[h].npools() == 0)
continue;
963 if (host_sched[h] == SchedStrategy::INF)
continue;
965 const std::size_t np = P.npools(),
nc = P.compat.cols();
966 std::size_t declared = 0;
967 for (std::size_t p = 0; p < np; ++p) {
968 const double cnt = (p < P.counts.size()) ? P.counts[p] : 0.0;
969 const std::size_t c = cnt > 0.0 ?
static_cast<std::size_t
>(cnt) : 0;
971 pool_capacity[h].push_back(
static_cast<double>(c) * (r > 0.0 ? r : 1.0));
973 std::vector<bool> sv(
nc,
false);
974 for (std::size_t j = 0; j <
nc; ++j)
976 pool_serves[h].push_back(sv);
978 if (declared != host_servers[h])
979 throw InputError(
"SolverLDES (native LN engine): processor '" +
lsn.names[h] +
980 "' declares server pools holding " + std::to_string(declared) +
981 " servers but has multiplicity " +
982 (host_servers[h] == std::numeric_limits<std::size_t>::max()
983 ? std::string(
"Inf") : std::to_string(host_servers[h])) +
984 "; the pools must partition the servers");
985 const std::vector<std::size_t>& ops =
986 (h <
lsn.tasksof.size()) ?
lsn.tasksof[h] : std::vector<std::size_t>();
987 for (std::size_t j = 0; j < ops.size() && j <
nc; ++j) pool_col[h][ops[j]] =
static_cast<int>(j);
993 std::priority_queue<LnEvent, std::vector<LnEvent>, LnEventLater> evq;
994 std::uint64_t seq = 0;
997 std::deque<LnLine> lines;
998 std::deque<LnInv> invs;
999 std::vector<std::size_t> free_lines, free_invs;
1000 std::vector<LnCustomer> custs;
1003 std::vector<std::size_t> host_busy(nhost_slots, 0);
1004 std::vector<std::deque<std::size_t>> host_queue(nhost_slots);
1006 std::vector<std::vector<std::size_t> > pool_jobs(nhost_slots);
1007 std::vector<double> pool_last(nhost_slots, 0.0), pool_rate(nhost_slots, 0.0);
1008 std::vector<std::uint64_t> pool_gen(nhost_slots, 0);
1009 std::vector<std::size_t> host_of_slot(nhost_slots, 0);
1010 for (std::size_t h =
lsn.hshift + 1; h <=
lsn.hshift +
lsn.nhosts; ++h)
1011 for (std::size_t m = 0; m < repl[h]; ++m) host_of_slot[host_slot0[h] + m] = h;
1013 std::vector<std::vector<int> > hocc(nhost_slots), tocc(ntask_slots);
1014 std::vector<std::deque<std::size_t> > hadm(nhost_slots), tadm(ntask_slots);
1015 for (std::size_t h =
lsn.hshift + 1; h <=
lsn.hshift +
lsn.nhosts; ++h)
1017 for (std::size_t m = 0; m < repl[h]; ++m)
1018 hocc[host_slot0[h] + m].assign(lincon[h].A[0].size(), 0);
1022 std::vector<std::vector<bool>> thr_held(ntask_slots);
1023 std::vector<std::vector<ThreadState>> thr_state(ntask_slots);
1024 std::vector<std::vector<double>> thr_setup_t0(ntask_slots);
1025 std::vector<std::vector<std::uint64_t>> thr_gen(ntask_slots);
1026 std::vector<std::deque<std::size_t>> thr_queue(ntask_slots);
1027 std::vector<std::size_t> thr_owner(ntask_slots, 0);
1028 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
1029 const std::size_t n = (task_threads[t] == std::numeric_limits<std::size_t>::max())
1030 ? 0 : task_threads[t];
1031 for (std::size_t m = 0; m < repl[t]; ++m) {
1032 const std::size_t ts = task_slot(t, m);
1034 thr_held[ts].assign(n,
false);
1035 thr_state[ts].assign(n, ThreadState::ACTIVE);
1036 thr_setup_t0[ts].assign(n, 0.0);
1037 thr_gen[ts].assign(n, 0);
1038 if (has_lincon[t]) tocc[ts].assign(lincon[t].A[0].size(), 0);
1045 std::vector<Sampler> setupd(nidx + 1), delayoffd(nidx + 1);
1046 std::vector<bool> has_setup(nidx + 1,
false);
1047 std::vector<Rng> g_setup, g_doff;
1048 g_setup.reserve(nidx + 1);
1049 g_doff.reserve(nidx + 1);
1050 for (std::size_t k = 0; k <= nidx; ++k) {
1051 g_setup.push_back(
engine::ln_rng(ln_seed,
static_cast<long long>(k) * 10 + 2000));
1052 g_doff.push_back(
engine::ln_rng(ln_seed,
static_cast<long long>(k) * 10 + 3000));
1054 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
1055 if (t >=
lsn.hassetup.size() || !
lsn.hassetup[t])
continue;
1056 const bool su = t <
lsn.setuptime.size() && !
lsn.setuptime[t].disabled &&
1058 const bool df = t <
lsn.delayofftime.size() && !
lsn.delayofftime[t].disabled &&
1060 if (!su || !df)
continue;
1061 setupd[t] = Sampler(
lsn.setuptime[t],
"the setup time of '" +
lsn.names[t] +
"'");
1062 delayoffd[t] = Sampler(
lsn.delayofftime[t],
"the delay-off time of '" +
lsn.names[t] +
"'");
1063 has_setup[t] =
true;
1073 std::vector<Rng> g_route, g_item, g_rr;
1074 g_route.reserve(nidx + 1);
1075 for (std::size_t k = 0; k <= nidx; ++k)
1076 g_route.push_back(
engine::ln_rng(ln_seed, 940000 +
static_cast<long long>(k) * 10));
1077 for (std::size_t c = 0; c < caches.size(); ++c) {
1078 g_item.push_back(
engine::ln_rng(ln_seed, 950000 +
static_cast<long long>(c) * 10));
1079 g_rr.push_back(
engine::ln_rng(ln_seed, 960000 +
static_cast<long long>(c) * 10));
1089 std::vector<std::vector<std::size_t> > fwd_dst(nidx + 1);
1090 std::vector<std::vector<double> > fwd_prob(nidx + 1);
1091 for (std::size_t c = 1; c <=
lsn.ncalls && c <
lsn.calltype.size(); ++c) {
1093 const std::size_t src =
lsn.callpair_src[c], dst =
lsn.callpair_dst[c];
1094 if (src == 0 || dst == 0 || src > nidx || dst > nidx)
continue;
1095 fwd_dst[src].push_back(dst);
1096 fwd_prob[src].push_back(c <
lsn.callproc_mean.size()
1100 std::vector<Rng> g_fwd;
1101 g_fwd.reserve(nidx + 1);
1102 for (std::size_t k = 0; k <= nidx; ++k)
1103 g_fwd.push_back(
engine::ln_rng(ln_seed, 970000 +
static_cast<long long>(k) * 10));
1107 std::deque<std::size_t> runnable;
1117 auto task_prio = [&](std::size_t task) ->
int {
1118 return (task <
lsn.prio.size()) ?
lsn.prio[task] : 0;
1120 auto host_req_prio = [&](std::size_t lid) ->
int {
return task_prio(
lsn.parent[lines[lid].act]); };
1121 auto task_req_prio = [&](std::size_t vi) ->
int {
1122 const std::size_t c = invs[vi].caller;
1123 return c == LN_NONE ? 0 : task_prio(invs[lines[c].inv].task);
1129 auto fcfs_prio = [](SchedStrategy sc) {
1131 return sc == SchedStrategy::HOL || sc == SchedStrategy::FCFSPRPRIO ||
1132 sc == SchedStrategy::FCFSPIPRIO;
1134 auto lcfs_prio = [](SchedStrategy sc) {
1135 return sc == SchedStrategy::LCFSPRIO || sc == SchedStrategy::LCFSPRPRIO ||
1136 sc == SchedStrategy::LCFSPIPRIO;
1138 auto preemptive = [](SchedStrategy sc) {
1139 return sc == SchedStrategy::FCFSPRPRIO || sc == SchedStrategy::FCFSPIPRIO ||
1140 sc == SchedStrategy::LCFSPRPRIO || sc == SchedStrategy::LCFSPIPRIO;
1142 auto is_ps = [](SchedStrategy sc) {
1143 return sc == SchedStrategy::PS || sc == SchedStrategy::PSPRIO;
1150 auto prio_enqueue = [](std::deque<std::size_t>& q, std::size_t id,
1151 const std::function<int(std::size_t)>& prio_of,
bool lifo,
1152 const std::function<double(std::size_t)>* key_of) {
1153 const int prio = prio_of(
id);
1154 const double key = key_of ? (*key_of)(id) : 0.0;
1155 std::deque<std::size_t>::iterator it = q.end();
1156 while (it != q.begin()) {
1157 const std::size_t o = *(it - 1);
1158 const int po = prio_of(o);
1159 if (po > prio)
break;
1161 if (!lifo && (!key_of || (*key_of)(o) <= key))
break;
1162 if (lifo && key_of && (*key_of)(o) > key)
break;
1173 auto enqueue = [&](std::deque<std::size_t>& q, std::size_t id, SchedStrategy sc,
1174 const std::function<int(std::size_t)>& prio_of,
double& key, Rng& g_siro,
1175 const std::function<double(std::size_t)>* key_of) {
1176 if (fcfs_prio(sc) || lcfs_prio(sc)) {
1177 prio_enqueue(q,
id, prio_of, lcfs_prio(sc), key_of);
1178 }
else if (sc == SchedStrategy::LCFS) {
1181 if (sc == SchedStrategy::SIRO) key = uniform01(g_siro);
1186 auto dequeue = [&](std::deque<std::size_t>& q, SchedStrategy sc,
1187 const std::function<double(std::size_t)>& key_of) -> std::size_t {
1188 std::deque<std::size_t>::iterator best = q.begin();
1189 if (sc == SchedStrategy::SIRO)
1190 for (std::deque<std::size_t>::iterator it = q.begin(); it != q.end(); ++it)
1191 if (key_of(*it) < key_of(*best)) best = it;
1192 const std::size_t
id = *best;
1196 const std::function<double(std::size_t)> line_key = [&](std::size_t lid) {
1197 return lines[lid].siro_key;
1199 const std::function<double(std::size_t)> inv_key = [&](std::size_t vi) {
1200 return invs[vi].siro_key;
1202 const std::function<double(std::size_t)> host_key = [&](std::size_t lid) {
1203 return lines[lid].host_t0;
1212 std::vector<std::vector<std::size_t> > ps_jobs(nhost_slots);
1213 std::vector<double> ps_last(nhost_slots, 0.0);
1214 std::vector<std::uint64_t> ps_gen(nhost_slots, 0);
1220 auto ps_rates = [&](std::size_t hs) -> std::vector<double> {
1221 const std::vector<std::size_t>& pj = ps_jobs[hs];
1222 const std::size_t n = pj.size();
1223 const std::size_t host = host_of_slot[hs];
1224 const double c =
static_cast<double>(host_servers[host]);
1225 if (host_sched[host] != SchedStrategy::PSPRIO ||
static_cast<double>(n) <= c)
1226 return std::vector<double>(n, n == 0 ? 0.0 : std::min(1.0, c /
static_cast<double>(n)));
1227 std::vector<double> rate(n, 0.0);
1228 std::vector<int> pr(n);
1229 for (std::size_t i = 0; i < n; ++i) pr[i] = task_prio(
lsn.parent[lines[pj[i]].act]);
1230 std::vector<int> levels(pr);
1231 std::sort(levels.begin(), levels.end(), std::greater<int>());
1232 levels.erase(std::unique(levels.begin(), levels.end()), levels.end());
1234 for (std::size_t l = 0; l < levels.size() && left > 0.0; ++l) {
1235 std::size_t cnt = 0;
1236 for (std::size_t i = 0; i < n; ++i) cnt += (pr[i] == levels[l]) ? 1 : 0;
1237 const double alloc = std::min(left,
static_cast<double>(cnt));
1238 for (std::size_t i = 0; i < n; ++i)
1239 if (pr[i] == levels[l]) rate[i] = alloc /
static_cast<double>(cnt);
1245 auto ps_advance = [&](std::size_t hs) {
1246 const double dt = now - ps_last[hs];
1248 const std::vector<double> rate = ps_rates(hs);
1249 for (std::size_t i = 0; i < ps_jobs[hs].size(); ++i) {
1250 LnLine& L = lines[ps_jobs[hs][i]];
1251 L.remaining = std::max(0.0, L.remaining - dt * rate[i]);
1257 auto ps_busy = [&](std::size_t hs, std::size_t n) ->
double {
1258 return static_cast<double>(std::min(n, host_servers[host_of_slot[hs]]));
1262 std::vector<double> tot_q(nidx + 1, 0.0), tot_u(nidx + 1, 0.0);
1263 std::vector<double> cur_q(nidx + 1, 0.0), cur_u(nidx + 1, 0.0);
1264 std::vector<double> last_upd(nidx + 1, 0.0);
1265 std::vector<double> completions(nidx + 1, 0.0), resp_sum(nidx + 1, 0.0),
1266 resp_cnt(nidx + 1, 0.0);
1270 std::vector<std::vector<double> > entry_resp_samples(
lsn.nentries);
1278 std::vector<double> act_host_resid(nidx + 1, 0.0);
1283 std::vector<Rng> g_callmult;
1284 g_callmult.reserve(
lsn.ncalls + 1);
1285 for (std::size_t c = 0; c <=
lsn.ncalls; ++c)
1286 g_callmult.push_back(
engine::ln_rng(ln_seed,
static_cast<long long>(c) * 10 + 4000));
1297 std::vector<std::vector<std::size_t> > cg_members;
1298 std::vector<lang::RoutingStrategy> cg_strategy;
1299 std::vector<double> cg_mean;
1300 std::vector<std::vector<std::size_t> > cg_next;
1301 std::vector<long> cg_of(
lsn.ncalls + 1, -1);
1302 std::vector<bool> cg_lead(
lsn.ncalls + 1,
false);
1304 if (grp.
caller >=
lsn.callsof.size())
continue;
1305 const std::vector<std::size_t>& calls =
lsn.callsof[grp.
caller];
1306 std::vector<std::size_t> members;
1308 for (std::size_t target : grp.
targets) {
1309 for (std::size_t cidx : calls) {
1311 cg_of[cidx] < 0 && std::find(members.begin(), members.end(), cidx) == members.end()) {
1312 members.push_back(cidx);
1318 if (members.size() < 2)
continue;
1319 const long g =
static_cast<long>(cg_members.size());
1320 for (std::size_t cidx : members) cg_of[cidx] = g;
1321 for (std::size_t cidx : calls)
1322 if (cg_of[cidx] == g) {
1323 cg_lead[cidx] =
true;
1326 cg_members.push_back(members);
1327 cg_strategy.push_back(grp.
strategy);
1328 cg_mean.push_back(mean);
1329 cg_next.push_back(std::vector<std::size_t>(std::max<std::size_t>(1, repl[
lsn.parent[grp.
caller]]), 0));
1332 std::vector<Rng> g_cgcount;
1333 for (std::size_t g = 0; g < cg_members.size(); ++g)
1334 g_cgcount.push_back(
engine::ln_rng(ln_seed, 930000 +
static_cast<long long>(g)));
1337 std::vector<std::size_t> tslot_q(ntask_slots, 0);
1347 auto sample_call_count = [&](std::size_t cidx) -> std::size_t {
1348 const double m = (cidx <
lsn.callproc_mean.size())
1351 if (!(m > 0.0))
return 0;
1352 std::size_t n =
static_cast<std::size_t
>(std::floor(m));
1353 const double frac = m -
static_cast<double>(n);
1354 if (frac > 0.0 && cidx < g_callmult.size() && uniform01(g_callmult[cidx]) < frac) ++n;
1358 auto sample_group_count = [&](std::size_t g) -> std::size_t {
1359 const double m = cg_mean[g];
1360 if (!(m > 0.0))
return 0;
1361 std::size_t n =
static_cast<std::size_t
>(std::floor(m));
1362 const double frac = m -
static_cast<double>(n);
1363 if (frac > 0.0 && uniform01(g_cgcount[g]) < frac) ++n;
1368 auto dispatch_group = [&](std::size_t g, std::size_t caller_task, std::size_t crep,
1369 std::size_t& rep) -> std::size_t {
1370 const std::vector<std::size_t>& members = cg_members[g];
1372 std::vector<std::size_t>& next = cg_next[g];
1373 std::size_t& ptr = next[std::min(crep, next.size() - 1)];
1374 const std::size_t cidx = members[ptr];
1375 ptr = (ptr + 1) % members.size();
1376 rep = callee_replica(caller_task, crep,
lsn.parent[
lsn.callpair_dst[cidx]]);
1379 std::size_t best = members.front(), ties = 0, best_len = std::numeric_limits<std::size_t>::max();
1380 for (std::size_t cidx : members) {
1381 const std::size_t t =
lsn.parent[
lsn.callpair_dst[cidx]];
1382 const std::size_t r = callee_replica(caller_task, crep, t);
1383 const std::size_t len = tslot_q[task_slot(t, r)];
1384 if (len < best_len) {
1389 }
else if (len == best_len && uniform01(g_cgtie) *
static_cast<double>(++ties) < 1.0) {
1397 auto touch = [&](std::size_t k) {
1398 const double dt = now - last_upd[k];
1400 tot_q[k] += cur_q[k] * dt;
1401 tot_u[k] += cur_u[k] * dt;
1405 auto push_ev = [&](LnEvent e) {
1413 std::uint64_t done = 0;
1414 const std::uint64_t warm_events =
1415 static_cast<std::uint64_t
>(
static_cast<double>(max_events) * 0.2);
1417 double warm_t = 0.0;
1418 long long cycles = 0;
1425 auto reset_stats = [&]() {
1428 for (std::size_t k = 0; k <= nidx; ++k) {
1432 completions[k] = 0.0;
1435 act_host_resid[k] = 0.0;
1437 for (std::size_t e = 0; e < entry_resp_samples.size(); ++e) entry_resp_samples[e].clear();
1438 for (std::size_t ci = 0; ci < caches.size(); ++ci) {
1439 caches[ci].reads = 0;
1440 caches[ci].hits = 0;
1441 caches[ci].misses = 0;
1442 caches[ci].delayed = 0;
1448 auto new_line = [&](std::size_t inv, std::size_t act,
bool branch) -> std::size_t {
1450 if (!free_lines.empty()) {
1451 id = free_lines.back();
1452 free_lines.pop_back();
1453 lines[id] = LnLine();
1456 lines.push_back(LnLine());
1458 lines[id].inv = inv;
1459 lines[id].act = act;
1460 lines[id].calls_left = NOTDRAWN;
1461 lines[id].branch = branch;
1464 auto free_line = [&](std::size_t id) { free_lines.push_back(
id); };
1466 auto set_act = [&](std::size_t lid, std::size_t a) {
1467 LnLine& L = lines[lid];
1471 L.calls_left = NOTDRAWN;
1474 auto new_inv =[&](std::size_t entry, std::size_t trep, std::size_t caller,
1475 std::size_t cust) -> std::size_t {
1477 const bool pre = !warm || (caller != LN_NONE && invs[lines[caller].inv].pre);
1479 if (!free_invs.empty()) {
1480 id = free_invs.back();
1481 free_invs.pop_back();
1485 invs.push_back(LnInv());
1487 LnInv& v = invs[id];
1489 v.task =
lsn.parent[entry];
1491 v.ts = task_slot(v.task, trep);
1496 const std::size_t root = new_line(
id, bound[entry],
false);
1497 invs[id].root = root;
1505 auto enter_task = [&](std::size_t vi) {
1506 LnInv& v = invs[vi];
1509 cur_q[v.task] += 1.0;
1517 auto arrive_at_task = [&](std::size_t vi) ->
bool {
1518 LnInv& v = invs[vi];
1519 const int col = lincon_col(v.task, v.entry);
1522 if (!admits(v.task, tocc[v.ts], col)) {
1523 tadm[v.ts].push_back(vi);
1526 ++tocc[v.ts][
static_cast<std::size_t
>(col)];
1533 std::function<void(std::size_t)> advance;
1535 auto drain = [&]() {
1536 while (!runnable.empty()) {
1537 const std::size_t
id = runnable.front();
1538 runnable.pop_front();
1544 std::function<void(std::size_t)> advance_pool;
1545 auto pool_rates = [&](std::size_t hs) {
1546 const std::size_t h = host_of_slot[hs];
1547 const std::vector<std::size_t>& jobs = pool_jobs[hs];
1548 std::vector<double> rate(jobs.size(), 0.0);
1549 for (std::size_t p = 0; p < pool_capacity[h].size(); ++p)
1550 for (std::size_t i = 0; i < jobs.size(); ++i) {
1551 const int j = lines[jobs[i]].pcol;
1552 if (j >= 0 &&
static_cast<std::size_t
>(j) < pool_serves[h][p].size() &&
1553 pool_serves[h][p][
static_cast<std::size_t
>(j)]) {
1554 rate[i] += pool_capacity[h][p];
1561 advance_pool = [&](std::size_t hs) {
1562 const std::vector<double> r = pool_rates(hs);
1563 const double dt = now - pool_last[hs];
1564 for (std::size_t i = 0; i < pool_jobs[hs].size(); ++i) {
1565 LnLine& L = lines[pool_jobs[hs][i]];
1566 L.remaining = std::max(0.0, L.remaining - dt * r[i]);
1568 pool_last[hs] = now;
1575 auto rearm_pool = [&](std::size_t hs) {
1576 const std::size_t h = host_of_slot[hs];
1577 const std::vector<double> r = pool_rates(hs);
1578 double tot = 0.0, tmin = std::numeric_limits<double>::infinity();
1579 for (std::size_t i = 0; i < r.size(); ++i) {
1581 if (r[i] > 0.0) tmin = std::min(tmin, lines[pool_jobs[hs][i]].remaining / r[i]);
1584 pool_rate[hs] = tot;
1586 for (std::size_t m = 0; m < repl[h]; ++m) u += pool_rate[host_slot0[h] + m];
1588 const std::uint64_t g = ++pool_gen[hs];
1589 if (std::isfinite(tmin)) {
1599 auto ps_schedule = [&](std::size_t hs) {
1601 if (ps_jobs[hs].empty())
return;
1602 const std::vector<double> rate = ps_rates(hs);
1603 double tmin = std::numeric_limits<double>::infinity();
1604 for (std::size_t i = 0; i < ps_jobs[hs].size(); ++i)
1605 if (rate[i] > 0.0) tmin = std::min(tmin, lines[ps_jobs[hs][i]].remaining / rate[i]);
1615 std::vector<std::vector<std::size_t> > in_service(nhost_slots);
1616 std::uint64_t host_gen_ctr = 0;
1618 std::uint64_t fork_ctr = 0;
1623 auto begin_host = [&](std::size_t lid, std::size_t hs) {
1624 LnLine& L = lines[lid];
1625 if (L.host_rem < 0.0) L.host_rem = hostdem[L.act].next(g_host[L.act]);
1627 L.host_end = now + L.host_rem;
1628 if (preemptive(host_sched[host_of_slot[hs]])) in_service[hs].push_back(lid);
1629 L.host_gen = ++host_gen_ctr;
1644 auto preempt_for = [&](std::size_t lid, std::size_t hs) ->
bool {
1645 const SchedStrategy sc = host_sched[host_of_slot[hs]];
1646 const bool lifo = lcfs_prio(sc);
1647 const int pa = host_req_prio(lid);
1648 std::vector<std::size_t>& ins = in_service[hs];
1649 std::size_t vi = ins.size();
1651 for (std::size_t i = 0; i < ins.size(); ++i) {
1652 const int pr = host_req_prio(ins[i]);
1653 if (pr >= pa)
continue;
1654 bool better = vi == ins.size() || pr < pv;
1655 if (!better && pr == pv) {
1656 const LnLine& r = lines[ins[i]];
1657 const LnLine& v = lines[ins[vi]];
1658 better = lifo ? r.host_t0 < v.host_t0 : r.host_start > v.host_start;
1665 if (vi == ins.size() && lifo)
1666 for (std::size_t i = 0; i < ins.size(); ++i)
1667 if (host_req_prio(ins[i]) == pa &&
1668 (vi == ins.size() || lines[ins[i]].host_start < lines[ins[vi]].host_start))
1670 if (vi == ins.size())
return false;
1671 const std::size_t victim = ins[vi];
1672 ins.erase(ins.begin() +
static_cast<std::ptrdiff_t
>(vi));
1673 LnLine& v = lines[victim];
1674 v.host_gen = ++host_gen_ctr;
1675 const bool resume = sc == SchedStrategy::FCFSPRPRIO || sc == SchedStrategy::LCFSPRPRIO;
1676 v.host_rem = resume ? std::max(0.0, v.host_end - now) : -1.0;
1677 enqueue(host_queue[hs], victim, sc, host_req_prio, v.siro_key, g_siro_host, &host_key);
1678 begin_host(lid, hs);
1682 auto start_host_service = [&](std::size_t lid) {
1683 LnLine& L = lines[lid];
1684 const std::size_t act = L.act;
1685 const std::size_t host =
lsn.host_of(act);
1686 const std::size_t hs = host_slot(host, invs[L.inv].trep);
1691 const std::size_t t =
lsn.parent[act];
1692 const std::map<std::size_t, int>::const_iterator it = pool_col[host].find(t);
1693 if (it == pool_col[host].end())
1694 throw InputError(
"SolverLDES (native LN engine): activity '" +
lsn.names[act] +
1695 "' runs on a host whose pool declaration does not name its "
1696 "task, so no server can be assigned");
1697 lines[lid].pcol = it->second;
1698 lines[lid].remaining = hostdem[act].next(g_host[act]);
1699 pool_jobs[hs].push_back(lid);
1703 lines[lid].host_rem = -1.0;
1704 if (is_ps(host_sched[host])) {
1707 const std::size_t n0 = ps_jobs[hs].size();
1708 lines[lid].remaining = hostdem[act].next(g_host[act]);
1709 ps_jobs[hs].push_back(lid);
1711 cur_u[host] += ps_busy(hs, n0 + 1) - ps_busy(hs, n0);
1715 if (host_busy[hs] < host_servers[host]) {
1718 begin_host(lid, hs);
1719 }
else if (!(preemptive(host_sched[host]) && preempt_for(lid, hs))) {
1720 enqueue(host_queue[hs], lid, host_sched[host], host_req_prio, lines[lid].siro_key,
1721 g_siro_host, &host_key);
1730 auto request_host = [&](std::size_t lid) {
1731 LnLine& L = lines[lid];
1732 const std::size_t act = L.act;
1733 const std::size_t host =
lsn.host_of(act);
1734 const std::size_t hs = host_slot(host, invs[L.inv].trep);
1736 const int col = lincon_col(host,
lsn.parent[act]);
1739 if (!admits(host, hocc[hs], col)) {
1740 hadm[hs].push_back(lid);
1743 ++hocc[hs][
static_cast<std::size_t
>(col)];
1745 start_host_service(lid);
1748 auto release_host_admission = [&](std::size_t lid) {
1749 LnLine& L = lines[lid];
1750 if (L.hcol < 0)
return;
1751 const std::size_t host =
lsn.host_of(L.act);
1752 const std::size_t hs = host_slot(host, invs[L.inv].trep);
1753 --hocc[hs][
static_cast<std::size_t
>(L.hcol)];
1755 while (!hadm[hs].empty()) {
1756 const std::size_t head = hadm[hs].front();
1757 const int c = lines[head].hcol;
1758 if (!admits(host, hocc[hs], c))
return;
1759 hadm[hs].pop_front();
1760 ++hocc[hs][
static_cast<std::size_t
>(c)];
1761 start_host_service(head);
1766 auto start_setup = [&](std::size_t ts, std::size_t th) {
1767 const std::size_t task = thr_owner[ts];
1768 thr_state[ts][th] = ThreadState::SETUP;
1769 thr_setup_t0[ts][th] = now;
1771 e.t = now + setupd[task].next(g_setup[task]);
1775 e.gen = ++thr_gen[ts][th];
1780 auto start_delayoff = [&](std::size_t ts, std::size_t th) {
1781 const std::size_t task = thr_owner[ts];
1782 thr_state[ts][th] = ThreadState::DELAYOFF;
1784 e.t = now + delayoffd[task].next(g_doff[task]);
1788 e.gen = ++thr_gen[ts][th];
1801 auto acquire_thread = [&](std::size_t vi) ->
bool {
1802 LnInv& v = invs[vi];
1803 const std::size_t task = v.task;
1804 const std::size_t ts = v.ts;
1806 if (task_threads[task] == std::numeric_limits<std::size_t>::max()) {
1808 v.has_thread =
true;
1812 std::vector<bool>& held = thr_held[ts];
1813 std::vector<ThreadState>& st = thr_state[ts];
1817 for (std::size_t th = 0; th < held.size(); ++th) {
1818 if (!held[th] && st[th] == ThreadState::ACTIVE) {
1821 v.has_thread =
true;
1826 for (std::size_t th = 0; th < held.size(); ++th) {
1827 if (!held[th] && st[th] == ThreadState::DELAYOFF) {
1829 st[th] = ThreadState::ACTIVE;
1832 v.has_thread =
true;
1838 v.cold_wait =
static_cast<std::size_t
>(std::count(held.begin(), held.end(),
true)) < held.size();
1839 enqueue(thr_queue[ts], vi, task_sched[task], task_req_prio, invs[vi].siro_key, g_siro_task,
1841 for (std::size_t th = 0; th < held.size(); ++th) {
1842 if (!held[th] && st[th] == ThreadState::OFF) {
1843 start_setup(ts, th);
1851 auto release_thread = [&](std::size_t vi) {
1852 LnInv& v = invs[vi];
1853 const std::size_t ts = v.ts;
1854 const std::size_t th = v.th;
1855 const std::size_t task = thr_owner[ts];
1858 v.has_thread =
false;
1860 if (th == NOTHR)
return;
1861 thr_held[ts][th] =
false;
1862 if (!thr_queue[ts].empty()) {
1863 const std::size_t nxt = dequeue(thr_queue[ts], task_sched[task], inv_key);
1864 thr_held[ts][th] =
true;
1866 invs[nxt].has_thread =
true;
1869 if (invs[nxt].cold_wait && !invs[nxt].started) {
1870 LnInv& w = invs[nxt];
1872 w.entry_t0 = w.task_t0;
1874 tot_q[w.entry] += now - std::max(w.entry_t0, warm_t);
1875 cur_q[w.entry] += 1.0;
1877 runnable.push_back(invs[nxt].root);
1878 }
else if (has_setup[task]) {
1879 start_delayoff(ts, th);
1894 auto finish_act = [&](LnLine& L) {
1895 const std::size_t act = L.act;
1896 const bool seen = !invs[L.inv].pre;
1897 if (seen) completions[act] += 1.0;
1898 if (L.act_t0 >= 0.0) {
1900 resp_sum[act] += now - L.act_t0;
1901 resp_cnt[act] += 1.0;
1920 auto send_reply = [&](std::size_t vi,
bool direct) {
1921 LnInv& v = invs[vi];
1922 if (v.replied)
return;
1924 v.early_reply = !direct;
1926 completions[v.entry] += 1.0;
1927 const double r = now - v.entry_t0;
1928 resp_sum[v.entry] += r;
1929 resp_cnt[v.entry] += 1.0;
1933 if (v.entry >
lsn.eshift && v.entry <=
lsn.eshift +
lsn.nentries)
1934 entry_resp_samples[v.entry -
lsn.eshift - 1].push_back(r);
1940 if (v.caller != LN_NONE && !fwd_dst[v.entry].empty()) {
1941 const double u = uniform01(g_fwd[v.entry]);
1943 for (std::size_t k = 0; k < fwd_dst[v.entry].size(); ++k) {
1944 cum += fwd_prob[v.entry][k];
1946 const std::size_t tgt = fwd_dst[v.entry][k];
1947 const std::size_t caller = v.caller;
1949 const std::size_t rep = callee_replica(v.task, v.trep,
lsn.parent[tgt]);
1951 const std::size_t child = new_inv(tgt, rep, caller, LN_NONE);
1952 if (arrive_at_task(child)) runnable.push_back(invs[child].root);
1957 if (!direct && v.caller != LN_NONE) runnable.push_back(v.caller);
1962 auto cache_insert = [&](std::size_t ci, std::size_t item) {
1963 LnCacheState& cs = caches[ci];
1964 std::list<std::size_t>& lst = cs.levels[0];
1965 const std::size_t cap =
static_cast<std::size_t
>(std::max(0, cs.cap[0]));
1967 if (lst.size() >= cap && !lst.empty()) {
1968 std::size_t r =
static_cast<std::size_t
>(uniform01(g_rr[ci]) *
1969 static_cast<double>(lst.size()));
1970 r = std::min(r, lst.size() - 1);
1971 std::list<std::size_t>::iterator it = lst.begin();
1972 std::advance(it,
static_cast<long>(r));
1975 lst.push_front(item);
1978 lst.push_front(item);
1979 if (lst.size() > cap) lst.pop_back();
1983 auto cache_promote = [&](std::size_t ci, std::size_t item, std::size_t level) {
1984 LnCacheState& cs = caches[ci];
1986 const std::size_t inew = std::min(level + 1, cs.levels.size() - 1);
1990 if (inew <= level) {
1992 cs.levels[level].remove(item);
1993 cs.levels[level].push_front(item);
1997 std::list<std::size_t>& lo = cs.levels[level];
1998 std::list<std::size_t>& hi = cs.levels[inew];
1999 std::size_t kpos = 0;
2000 for (std::list<std::size_t>::iterator it = lo.begin(); it != lo.end(); ++it, ++kpos)
2001 if (*it == item)
break;
2003 if (hi.size() <
static_cast<std::size_t
>(std::max(0, cs.cap[inew]))) {
2004 hi.push_front(item);
2007 std::size_t demoted;
2009 std::size_t r =
static_cast<std::size_t
>(uniform01(g_rr[ci]) *
2010 static_cast<double>(hi.size()));
2011 r = std::min(r, hi.size() - 1);
2012 std::list<std::size_t>::iterator it = hi.begin();
2013 std::advance(it,
static_cast<long>(r));
2017 demoted = hi.back();
2019 hi.push_front(item);
2022 lo.push_front(demoted);
2024 std::list<std::size_t>::iterator it = lo.begin();
2025 std::advance(it,
static_cast<long>(std::min(kpos, lo.size())));
2026 lo.insert(it, demoted);
2029 static const std::size_t HELD =
static_cast<std::size_t
>(-2);
2036 auto access_cache = [&](std::size_t ai, std::size_t lid) -> std::size_t {
2037 const LnCacheAccess& ca = accesses[ai];
2038 LnInv& v = invs[lines[lid].inv];
2039 const std::size_t ci = ca.caches[std::min(v.trep, ca.caches.size() - 1)];
2040 LnCacheState& cs = caches[ci];
2042 const bool seen = !v.pre;
2043 if (seen) ++cs.reads;
2044 const double u = uniform01(g_item[ci]);
2045 std::size_t item = ca.cdf.size() - 1;
2046 for (std::size_t i = 0; i < ca.cdf.size(); ++i)
2047 if (u <= ca.cdf[i]) {
2051 for (std::size_t l = 0; l < cs.levels.size(); ++l)
2052 if (std::find(cs.levels[l].begin(), cs.levels[l].end(), item) != cs.levels[l].end()) {
2053 if (seen) ++cs.hits;
2054 cache_promote(ci, item, l);
2058 if (item < cs.inflight.size() && cs.inflight[item]) {
2059 cs.held[item].push_back(std::make_pair(lid, ai));
2062 if (seen) ++cs.misses;
2063 cs.inflight[item] = 1;
2066 v.fetch_item = item;
2069 if (seen) ++cs.misses;
2070 cache_insert(ci, item);
2074 auto release_fetch = [&](std::size_t vi) {
2075 LnInv& v = invs[vi];
2076 if (!v.fetching)
return;
2078 LnCacheState& cs = caches[v.fetch_cache];
2079 const std::size_t item = v.fetch_item;
2080 cs.inflight[item] = 0;
2081 cache_insert(v.fetch_cache, item);
2082 std::vector<std::pair<std::size_t, std::size_t> > held;
2083 held.swap(cs.held[item]);
2086 for (std::size_t k = 0; k < held.size(); ++k) {
2087 if (!invs[lines[held[k].first].inv].pre) ++caches[v.fetch_cache].delayed;
2088 set_act(held[k].first, accesses[held[k].second].hit);
2089 runnable.push_back(held[k].first);
2100 auto complete_request = [&](std::size_t vi) -> std::size_t {
2101 send_reply(vi,
true);
2103 LnInv& v = invs[vi];
2105 cur_q[v.entry] -= 1.0;
2106 const bool seen = !v.pre;
2107 if (seen) completions[v.task] += 1.0;
2109 cur_q[v.task] -= 1.0;
2113 const std::size_t ts = v.ts, task = v.task;
2114 --tocc[ts][
static_cast<std::size_t
>(v.tcol)];
2116 while (!tadm[ts].empty()) {
2117 const std::size_t head = tadm[ts].front();
2118 const int c = invs[head].tcol;
2119 if (!admits(task, tocc[ts], c))
break;
2120 tadm[ts].pop_front();
2121 ++tocc[ts][
static_cast<std::size_t
>(c)];
2123 runnable.push_back(invs[head].root);
2126 LnInv& w = invs[vi];
2127 const std::size_t caller = w.caller, cust = w.cust;
2128 const bool early = w.early_reply;
2130 free_invs.push_back(vi);
2131 if (cust != LN_NONE) {
2135 const std::size_t rt = custs[cust].ref_task;
2137 resp_sum[rt] += now - custs[cust].t_start;
2138 resp_cnt[rt] += 1.0;
2142 e.t = now + (has_think[rt] ? think[rt].next(g_think[rt]) : 0.0);
2148 return (caller != LN_NONE && !early) ? caller : LN_NONE;
2152 auto finish_line = [&](std::size_t lid) -> std::size_t {
2153 const std::size_t vi = lines[lid].inv;
2154 LnInv& v = invs[vi];
2155 const bool br = lines[lid].branch;
2156 if (v.pending < 0)
return complete_request(vi);
2157 const long rem = v.pending - 1;
2158 if (br) free_line(lid);
2160 invs[vi].pending = -1;
2161 return complete_request(vi);
2163 invs[vi].pending = rem;
2175 auto complete_activity = [&](std::size_t lid) -> std::size_t {
2176 finish_act(lines[lid]);
2180 if (!warm && done >= warm_events) reset_stats();
2181 const std::size_t act = lines[lid].act;
2182 const std::size_t vi = lines[lid].inv;
2183 if (access_of_driver[act] != LN_NONE) {
2184 const std::size_t nx = access_cache(access_of_driver[act], lid);
2185 if (nx == HELD)
return LN_NONE;
2189 const std::vector<std::size_t>& sc = succ_act[act];
2190 const bool br = lines[lid].branch;
2193 const std::size_t j = sc[0];
2194 const std::uint64_t fid = lines[lid].fork_id;
2198 LnJoin& jn = invs[vi].joins[j];
2199 std::map<std::pair<std::size_t, std::uint64_t>,
long>::iterator it =
2200 jn.discard.find(std::make_pair(act, fid));
2201 if (it == jn.discard.end()) it = jn.discard.find(std::make_pair(act, LN_ANYFORK));
2202 if (it != jn.discard.end()) {
2203 if (--it->second <= 0) jn.discard.erase(it);
2204 if (jn.got.empty() && jn.discard.empty()) invs[vi].joins.erase(j);
2205 return finish_line(lid);
2208 LnJoin& jn = invs[vi].joins[j];
2210 if (br) free_line(lid);
2211 const std::size_t req = join_quorum[j] > 0 ? join_quorum[j] : 1;
2212 if (jn.got.size() >= req) {
2215 std::uint64_t rf = jn.got.begin()->second;
2216 for (
const std::pair<const std::size_t, std::uint64_t>& g : jn.got)
2217 if (g.second != rf) rf = LN_ANYFORK;
2218 for (std::size_t in : join_inputs[j])
2219 if (jn.got.find(in) == jn.got.end()) ++jn.discard[std::make_pair(in, rf)];
2221 if (jn.discard.empty()) invs[vi].joins.erase(j);
2222 if (invs[vi].pending >= 0) invs[vi].pending -=
static_cast<long>(req - 1);
2223 set_act(invs[vi].root, j);
2224 return invs[vi].root;
2228 if (!invs[vi].replied && !br) {
2229 bool reply = branch_reply[act] != 0 && branch_reply[act] == invs[vi].entry;
2230 if (!reply && phase_of(act) == 1)
2231 for (std::size_t s : sc)
2232 if (phase_of(s) == 2) {
2239 if (!reply && sc.empty() && invs[vi].pending > 1) reply =
true;
2240 if (reply) send_reply(vi,
false);
2242 if (invs[vi].replied && invs[vi].fetching) release_fetch(vi);
2243 if (sc.empty())
return finish_line(lid);
2244 if (sc.size() == 1) {
2245 set_act(lid, sc[0]);
2248 if (!or_prob[act].empty()) {
2249 const double u = uniform01(g_route[act]);
2251 std::size_t pick = sc.back();
2252 for (std::size_t k = 0; k < sc.size(); ++k) {
2253 cum += or_prob[act][k];
2264 invs[vi].pending = (invs[vi].pending < 0 ? 1 : invs[vi].pending) +
2265 static_cast<long>(sc.size()) - 1;
2266 if (br) free_line(lid);
2267 const std::uint64_t fork = ++fork_ctr;
2268 std::vector<std::size_t> kids;
2269 for (std::size_t k = 0; k < sc.size(); ++k) {
2270 kids.push_back(new_line(vi, sc[k],
true));
2271 lines[kids.back()].fork_id = fork;
2273 for (std::size_t k = kids.size(); k-- > 1;) runnable.push_front(kids[k]);
2281 auto spawn_request = [&](std::size_t entry, std::size_t rep,
bool pre) {
2282 const std::size_t vi = new_inv(entry, rep, LN_NONE, LN_NONE);
2284 if (pre) invs[vi].pre =
true;
2285 if (arrive_at_task(vi)) runnable.push_back(invs[vi].root);
2292 advance = [&](std::size_t lid) {
2293 while (lid != LN_NONE) {
2294 const std::size_t vi = lines[lid].inv;
2297 if (!invs[vi].has_thread) {
2298 if (!acquire_thread(vi))
return;
2302 if (!invs[vi].started) {
2303 LnInv& v = invs[vi];
2307 cur_q[v.entry] += 1.0;
2309 LnLine& L = lines[lid];
2310 const std::size_t act = L.act;
2312 lid = complete_request(vi);
2315 if (L.act_t0 < 0.0) {
2323 if (L.dem_done == 0) {
2325 if (has_hostdem[act]) {
2332 if (L.dem_done == 1) {
2334 if (has_actthink[act]) {
2336 e.t = now + actthink[act].next(g_actthink[act]);
2343 const std::vector<std::size_t>& calls =
2344 (act <
lsn.callsof.size()) ?
lsn.callsof[act] : std::vector<std::size_t>();
2345 if (L.call_pos < calls.size()) {
2348 const std::size_t cidx = calls[L.call_pos];
2349 const long grp = cg_of[cidx];
2350 if (grp >= 0 && !cg_lead[cidx]) {
2355 if (L.calls_left == NOTDRAWN)
2356 L.calls_left = (grp >= 0) ? sample_group_count(
static_cast<std::size_t
>(grp)) : sample_call_count(cidx);
2357 if (L.calls_left == 0) {
2359 L.calls_left = NOTDRAWN;
2363 if (L.calls_left == 0) {
2365 L.calls_left = NOTDRAWN;
2367 std::size_t callee =
lsn.callpair_dst[cidx];
2370 callee =
lsn.callpair_dst[dispatch_group(
static_cast<std::size_t
>(grp), invs[vi].task,
2371 invs[vi].trep, rep)];
2373 rep = callee_replica(invs[vi].task, invs[vi].trep,
lsn.parent[callee]);
2377 const std::size_t child = new_inv(callee, rep, lid, LN_NONE);
2378 if (!arrive_at_task(child))
return;
2379 lid = invs[child].root;
2384 spawn_request(callee, rep, invs[vi].pre);
2390 L.calls_left = NOTDRAWN;
2391 lid = complete_activity(lid);
2398 auto start_cycle = [&](std::size_t c) {
2399 const std::size_t t = custs[c].ref_task;
2400 custs[c].t_start = now;
2401 const std::size_t vi = new_inv(
lsn.entriesof[t][0], custs[c].repl, LN_NONE, c);
2402 if (arrive_at_task(vi)) advance(invs[vi].root);
2404 bool any_ref =
false;
2405 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
2406 if (t >=
lsn.isref.size() || !
lsn.isref[t])
continue;
2408 const std::size_t n = (task_threads[t] == std::numeric_limits<std::size_t>::max())
2411 const std::vector<std::size_t>& es =
2412 (t <
lsn.entriesof.size()) ?
lsn.entriesof[t] : std::vector<std::size_t>();
2414 throw InputError(
"SolverLDES (native LN engine): reference task '" +
lsn.names[t] +
2415 "' declares no entry to invoke");
2418 for (std::size_t m = 0; m < repl[t]; ++m) {
2419 for (std::size_t k = 0; k < n; ++k) {
2423 custs.push_back(cu);
2424 start_cycle(custs.size() - 1);
2431 std::vector<Sampler> arrival(nidx + 1);
2432 std::vector<Rng> g_arr;
2433 g_arr.reserve(nidx + 1);
2434 for (std::size_t k = 0; k <= nidx; ++k)
2435 g_arr.push_back(
engine::ln_rng(ln_seed,
static_cast<long long>(k) * 10 + 5000));
2436 for (std::size_t e =
lsn.eshift + 1; e <=
lsn.eshift +
lsn.nentries; ++e) {
2437 if (e >=
lsn.has_arrival.size() || !
lsn.has_arrival[e] || e >=
lsn.arrival.size() ||
2438 lsn.arrival[e].disabled)
2440 arrival[e] = Sampler(
lsn.arrival[e],
"the open arrivals at '" +
lsn.names[e] +
"'");
2441 for (std::size_t m = 0; m < repl[
lsn.parent[e]]; ++m) {
2443 ev.t = now + arrival[e].next(g_arr[e]);
2452 throw InputError(
"SolverLDES (native LN engine): the model has no reference task, so "
2453 "nothing drives it");
2456 while (!evq.empty() && done < max_events) {
2457 const LnEvent ev = evq.top();
2464 start_cycle(ev.who);
2469 if (ev.kind == 2 || ev.kind == 3) {
2470 const std::size_t ts = ev.who, th = ev.aux;
2471 if (ev.gen != thr_gen[ts][th])
continue;
2473 if (thr_state[ts][th] == ThreadState::DELAYOFF)
2474 thr_state[ts][th] = ThreadState::OFF;
2479 thr_state[ts][th] = ThreadState::ACTIVE;
2480 if (!thr_held[ts][th] && !thr_queue[ts].empty()) {
2481 const std::size_t nxt = dequeue(thr_queue[ts], task_sched[thr_owner[ts]], inv_key);
2482 thr_held[ts][th] =
true;
2483 touch(thr_owner[ts]);
2484 cur_u[thr_owner[ts]] += 1.0;
2485 invs[nxt].has_thread =
true;
2492 if (!invs[nxt].started) {
2493 LnInv& w = invs[nxt];
2495 w.entry_t0 = w.cold_wait ? w.task_t0 : std::max(w.task_t0, thr_setup_t0[ts][th]);
2498 tot_q[w.entry] += now - std::max(w.entry_t0, warm_t);
2499 cur_q[w.entry] += 1.0;
2501 advance(invs[nxt].root);
2509 const std::size_t hs = ev.who;
2510 if (ev.gen != pool_gen[hs])
continue;
2511 const std::size_t host = host_of_slot[hs];
2513 std::vector<std::size_t> finished, keep;
2514 for (std::size_t lid : pool_jobs[hs])
2515 (lines[lid].remaining <= 1e-9 ? finished : keep).push_back(lid);
2516 pool_jobs[hs].swap(keep);
2518 for (std::size_t lid : finished) {
2521 if (!invs[lines[lid].inv].pre) {
2522 completions[host] += 1.0;
2523 act_host_resid[lines[lid].act] += now - lines[lid].host_t0;
2525 release_host_admission(lid);
2534 const std::size_t hs = ev.who;
2535 if (ev.gen != ps_gen[hs])
continue;
2536 const std::size_t host = host_of_slot[hs];
2538 std::vector<std::size_t> finished, keep;
2539 for (std::size_t lid : ps_jobs[hs])
2540 (lines[lid].remaining <= 1e-9 ? finished : keep).push_back(lid);
2541 const std::size_t n0 = ps_jobs[hs].size();
2542 ps_jobs[hs].swap(keep);
2544 cur_u[host] -= ps_busy(hs, n0) - ps_busy(hs, ps_jobs[hs].size());
2546 for (std::size_t lid : finished) {
2549 if (!invs[lines[lid].inv].pre) {
2550 completions[host] += 1.0;
2551 act_host_resid[lines[lid].act] += now - lines[lid].host_t0;
2553 release_host_admission(lid);
2569 spawn_request(ev.who, ev.aux,
false);
2571 nx.t = now + arrival[ev.who].next(g_arr[ev.who]);
2578 const std::size_t lid = ev.who;
2579 if (ev.gen != lines[lid].host_gen)
continue;
2580 const std::size_t act = lines[lid].act;
2581 const std::size_t host =
lsn.host_of(act);
2584 const std::size_t hs = host_slot(host, invs[lines[lid].inv].trep);
2589 if (!invs[lines[lid].inv].pre) {
2590 completions[host] += 1.0;
2591 act_host_resid[act] += now - lines[lid].host_t0;
2593 if (preemptive(host_sched[host]))
2594 in_service[hs].erase(std::find(in_service[hs].begin(), in_service[hs].end(), lid));
2595 if (!host_queue[hs].empty()) {
2596 const std::size_t nxt = dequeue(host_queue[hs], host_sched[host], line_key);
2599 begin_host(nxt, hs);
2601 release_host_admission(lid);
2611 if (done < max_events && evq.empty()) {
2612 for (std::size_t s = 0; s < nhost_slots; ++s)
2613 if (!hadm[s].empty())
2614 throw InputError(
"SolverLDES (native LN engine): the admission constraint on "
2615 "host '" +
lsn.names[host_of_slot[s]] +
"' deadlocked the model: " +
2616 std::to_string(hadm[s].size()) +
2617 " request(s) are blocked with no event left");
2618 for (std::size_t s = 0; s < ntask_slots; ++s)
2619 if (!tadm[s].empty())
2620 throw InputError(
"SolverLDES (native LN engine): the admission constraint on "
2621 "task '" +
lsn.names[thr_owner[s]] +
"' deadlocked the model: " +
2622 std::to_string(tadm[s].size()) +
2623 " request(s) are blocked with no event left");
2627 for (std::size_t k = 1; k <= nidx; ++k) touch(k);
2629 const double span = now - warm_t;
2641 res.WLN =
Matrix<double>(nidx + 1, 1, std::numeric_limits<double>::quiet_NaN());
2642 for (std::size_t k = 1; k <= nidx; ++k) {
2644 res.QLN(k, 0) = tot_q[k] / span;
2657 const bool is_task = (k >
lsn.tshift && k <=
lsn.tshift +
lsn.ntasks);
2658 const std::size_t cap_k = is_task ? task_threads[k] : host_servers[k];
2659 double c = (cap_k == std::numeric_limits<std::size_t>::max())
2661 :
static_cast<double>(is_task ? cap_k * repl[k] : cap_k);
2663 if (!is_task && k <
lsn.maxmult.size() &&
lsn.maxmult[k] > 0.0 &&
lsn.maxmult[k] < c)
2665 res.ULN(k, 0) = tot_u[k] / (span * c);
2666 if (is_task) res.UThreadLN(k, 0) = res.ULN(k, 0);
2667 res.TLN(k, 0) = completions[k] / span;
2669 if (is_task && cap_k != std::numeric_limits<std::size_t>::max() && res.TLN(k, 0) > 0.0) {
2670 const double pool =
static_cast<double>(cap_k * repl[k]);
2671 res.ZLN(k, 0) = std::max(0.0, (pool - tot_u[k] / span) / res.TLN(k, 0));
2673 if (k >
lsn.eshift && k <=
lsn.eshift +
lsn.nentries) res.ALN(k, 0) = res.TLN(k, 0);
2675 if (resp_cnt[k] > 0.0) res.RLN(k, 0) = resp_sum[k] / resp_cnt[k];
2677 res.entry_resp_samples.swap(entry_resp_samples);
2693 std::vector<double> task_resid(nidx + 1, 0.0);
2694 std::vector<bool> task_has_act(nidx + 1,
false);
2695 for (std::size_t a =
lsn.ashift + 1; a <=
lsn.ashift +
lsn.nacts && a <= nidx; ++a) {
2696 const std::size_t task =
lsn.parent[a];
2697 const double x_task = (task >= 1 && task <= nidx) ? res.TLN(task, 0) : 0.0;
2698 const double resid = (x_task > 0.0) ? act_host_resid[a] / (span * x_task) : 0.0;
2699 res.WLN(a, 0) = resid;
2700 if (task >= 1 && task <= nidx) {
2701 task_resid[task] += resid;
2702 task_has_act[task] =
true;
2705 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks && t <= nidx; ++t)
2706 if (task_has_act[t]) res.WLN(t, 0) = task_resid[t];
2725 auto host_mult = [&](std::size_t k) ->
double {
return lsn.host_reach_mult(k); };
2726 std::vector<double> proc_util(nidx + 1, 0.0), task_proc_util(nidx + 1, 0.0);
2727 for (std::size_t a =
lsn.ashift + 1; a <=
lsn.ashift +
lsn.nacts && a <= nidx; ++a) {
2728 const double d = (a <
lsn.hostdem.size() && !
lsn.hostdem[a].disabled)
2731 proc_util[a] = res.TLN(a, 0) * d;
2732 const std::size_t task =
lsn.parent[a];
2733 if (task >= 1 && task <= nidx) task_proc_util[task] += proc_util[a];
2735 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks && t <= nidx; ++t)
2736 res.ULN(t, 0) = task_proc_util[t] / host_mult(t);
2737 for (std::size_t e =
lsn.eshift + 1; e <=
lsn.eshift +
lsn.nentries && e <= nidx; ++e) {
2739 const std::vector<std::size_t>& acts =
2740 (e <
lsn.actsof.size()) ?
lsn.actsof[e] : std::vector<std::size_t>();
2741 for (std::size_t i = 0; i < acts.size(); ++i)
2742 if (acts[i] <= nidx) u += proc_util[acts[i]];
2743 res.ULN(e, 0) = u / host_mult(e);
2748 for (std::size_t a =
lsn.ashift + 1; a <=
lsn.ashift +
lsn.nacts && a <= nidx; ++a)
2749 res.ULN(a, 0) = proc_util[a] / host_mult(a);
2753 for (std::size_t ai = 0; ai < accesses.size(); ++ai) {
2754 long long reads = 0, hits = 0, misses = 0, delayed = 0;
2755 for (std::size_t ci : accesses[ai].caches) {
2756 reads += caches[ci].reads;
2757 hits += caches[ci].hits;
2758 misses += caches[ci].misses;
2759 delayed += caches[ci].delayed;
2761 if (reads <= 0 || !(span > 0.0))
continue;
2762 const double n =
static_cast<double>(reads);
2763 res.cache_task.push_back(
lsn.parent[accesses[ai].entry]);
2764 res.cache_entry.push_back(accesses[ai].entry);
2765 res.cache_hit.push_back(
static_cast<double>(hits) / n);
2766 res.cache_miss.push_back(
static_cast<double>(misses) / n);
2767 res.cache_delayed.push_back(
static_cast<double>(delayed) / n);
2768 res.cache_read_rate.push_back(n / span);
2771 res.simulated_time = span;
2772 res.warmup_time = warm_t;
2773 res.completions = cycles;
2774 res.events =
static_cast<long long>(done);