378 const std::size_t nidx =
lsn.nidx;
379 if (nidx == 0)
throw InputError(
"SolverLDES (native LN engine): empty layered model");
384 for (std::size_t k = 1; k <= nidx && k <
lsn.sched.size(); ++k) {
385 const bool is_host = k >
lsn.hshift && k <=
lsn.hshift +
lsn.nhosts;
386 const bool is_task = k >
lsn.tshift && k <=
lsn.tshift +
lsn.ntasks;
387 if (!is_host && !is_task)
continue;
388 const SchedStrategy sc =
lsn.sched[k];
389 const bool ok = sc == SchedStrategy::FCFS || sc == SchedStrategy::INF ||
390 sc == SchedStrategy::LCFS || sc == SchedStrategy::SIRO ||
391 sc == SchedStrategy::HOL || sc == SchedStrategy::REF ||
392 sc == SchedStrategy::LCFSPRIO ||
393 (is_host && (sc == SchedStrategy::PS || sc == SchedStrategy::PSPRIO ||
394 sc == SchedStrategy::FCFSPRPRIO ||
395 sc == SchedStrategy::FCFSPIPRIO ||
396 sc == SchedStrategy::LCFSPRPRIO ||
397 sc == SchedStrategy::LCFSPIPRIO));
400 for (std::size_t c = 0; c < scname.size(); ++c)
401 scname[c] =
static_cast<char>(std::toupper(
static_cast<unsigned char>(scname[c])));
403 "SolverLDES (native LN engine) does not simulate " + scname +
" scheduling at " +
404 (is_host ?
"processor" :
"task") +
" '" +
lsn.names[k] +
405 "'. The layered engine implements " +
406 (is_host ?
"FCFS, LCFS, SIRO, HOL, FCFSPRIO, LCFSPRIO, FCFSPRPRIO, FCFSPIPRIO, "
407 "LCFSPRPRIO, LCFSPIPRIO, PS, PSPRIO and INF"
408 :
"FCFS, LCFS, SIRO, HOL, FCFSPRIO, LCFSPRIO and INF (a task holds threads, "
409 "it does not divide or preempt them)") +
410 "; a weighted discipline would additionally need the per-task shares the layered "
411 "struct does not carry.");
413 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
416 if (t <
lsn.hassetup.size() &&
lsn.hassetup[t]) {
417 const double m = (t <
lsn.mult.size()) ?
lsn.mult[t] : 1.0;
420 "' declares a setup time on an infinite-server task, which "
421 "holds no thread to power down; give it a finite "
426 if (t <
lsn.pools.size() &&
lsn.pools[t].npools() > 0)
428 "' declares heterogeneous server pools; LDES holds server "
429 "pools on processors only");
432 const std::uint64_t max_events =
434 const std::uint64_t base =
435 (o.
seed >= 0) ?
static_cast<std::uint64_t
>(o.
seed) : std::random_device{}();
446 const long long ln_seed =
static_cast<long long>(base);
447 std::vector<Rng> g_host, g_think;
448 g_host.reserve(nidx + 1);
449 g_think.reserve(nidx + 1);
450 for (std::size_t k = 0; k <= nidx; ++k) {
451 g_host.push_back(Rng(ln_seed,
static_cast<long long>(k) * 10 + 1000));
452 g_think.push_back(Rng(ln_seed,
static_cast<long long>(k) * 10));
454 Rng g_call(ln_seed, 900000);
457 std::vector<Sampler> hostdem(nidx + 1), think(nidx + 1), actthink(nidx + 1);
458 std::vector<bool> has_hostdem(nidx + 1,
false), has_think(nidx + 1,
false),
459 has_actthink(nidx + 1,
false);
462 std::vector<Rng> g_actthink;
463 g_actthink.reserve(nidx + 1);
464 for (std::size_t k = 0; k <= nidx; ++k)
465 g_actthink.push_back(Rng(ln_seed,
static_cast<long long>(k) * 10 + 6000));
466 for (std::size_t a =
lsn.ashift + 1; a <=
lsn.ashift +
lsn.nacts && a <
lsn.actthink.size(); ++a) {
468 actthink[a] = Sampler(
lsn.actthink[a],
"the think time of activity '" +
lsn.names[a] +
"'");
469 has_actthink[a] =
true;
472 for (std::size_t k = 1; k <= nidx; ++k) {
473 if (k <
lsn.hostdem.size() && !
lsn.hostdem[k].disabled &&
475 hostdem[k] = Sampler(
lsn.hostdem[k],
"the host demand of '" +
lsn.names[k] +
"'");
476 has_hostdem[k] =
true;
478 if (k <
lsn.think.size() && !
lsn.think[k].disabled &&
480 think[k] = Sampler(
lsn.think[k],
"the think time of '" +
lsn.names[k] +
"'");
486 std::vector<std::size_t> host_servers(nidx + 1, 1), task_threads(nidx + 1, 1);
487 std::vector<SchedStrategy> host_sched(nidx + 1, SchedStrategy::FCFS);
488 std::vector<SchedStrategy> task_sched(nidx + 1, SchedStrategy::FCFS);
491 for (std::size_t h =
lsn.hshift + 1; h <=
lsn.hshift +
lsn.nhosts; ++h) {
492 const double m = (h <
lsn.mult.size()) ?
lsn.mult[h] : 1.0;
493 if (h <
lsn.sched.size()) host_sched[h] =
lsn.sched[h];
494 host_servers[h] = (std::isfinite(m) && host_sched[h] != SchedStrategy::INF)
495 ? std::max<std::size_t>(1,
static_cast<std::size_t
>(m + 0.5))
496 : std::numeric_limits<std::size_t>::max();
498 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
499 const double m = (t <
lsn.mult.size()) ?
lsn.mult[t] : 1.0;
500 if (t <
lsn.sched.size()) task_sched[t] =
lsn.sched[t];
501 task_threads[t] = (std::isfinite(m) && task_sched[t] != SchedStrategy::INF)
502 ?
static_cast<std::size_t
>(m + 0.5)
503 : std::numeric_limits<std::size_t>::max();
516 std::vector<std::size_t> repl(nidx + 1, 1);
517 for (std::size_t k = 1; k <
lsn.repl.size() && k <= nidx; ++k) {
518 const double r =
lsn.repl[k];
519 repl[k] = (r > 1.0) ?
static_cast<std::size_t
>(r + 0.5) : 1;
521 std::vector<std::size_t> host_slot0(nidx + 1, 0);
522 std::size_t nhost_slots = 0;
523 for (std::size_t h =
lsn.hshift + 1; h <=
lsn.hshift +
lsn.nhosts; ++h) {
524 host_slot0[h] = nhost_slots;
525 nhost_slots += repl[h];
527 if (nhost_slots == 0) nhost_slots = 1;
530 auto host_slot = [&](std::size_t host, std::size_t trep) {
531 return host_slot0[host] + (trep % repl[host]);
536 std::vector<std::size_t> task_slot0(nidx + 1, 0);
537 std::size_t ntask_slots = 0;
538 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
539 task_slot0[t] = ntask_slots;
540 ntask_slots += repl[t];
542 if (ntask_slots == 0) ntask_slots = 1;
543 auto task_slot = [&](std::size_t task, std::size_t trep) {
544 return task_slot0[task] + (trep % repl[task]);
556 auto callee_replica = [&](std::size_t caller_task, std::size_t crep,
557 std::size_t callee_task) -> std::size_t {
558 const std::size_t rb = repl[callee_task];
559 if (rb <= 1)
return 0;
560 std::size_t f =
static_cast<std::size_t
>(
lsn.fanout_at(caller_task, callee_task) + 0.5);
562 const std::size_t ra = repl[caller_task];
563 f = (rb > ra) ? std::max<std::size_t>(1, rb / ra) : 1;
565 f = std::min(std::max<std::size_t>(1, f), rb);
566 const std::size_t k =
567 (f > 1) ?
static_cast<std::size_t
>(uniform01(g_call) *
static_cast<double>(f)) : 0;
568 return ((crep * f) + std::min(k, f - 1)) % rb;
572 const std::size_t a_lo =
lsn.ashift + 1, a_hi =
lsn.ashift +
lsn.nacts;
573 auto is_act = [&](std::size_t k) {
return k >= a_lo && k <= a_hi; };
574 auto wgt = [&](std::size_t i, std::size_t j) {
577 auto pretype = [&](std::size_t a) {
580 auto posttype = [&](std::size_t a) {
583 auto phase_of = [&](std::size_t a) ->
int {
584 const std::size_t k = a -
lsn.ashift;
585 return (k <
lsn.actphase.size() &&
lsn.actphase[k] > 0) ?
lsn.actphase[k] : 1;
589 std::vector<std::vector<std::size_t> > succ_act(nidx + 1);
590 for (std::size_t a = a_lo; a <= a_hi; ++a)
591 for (std::size_t s :
lsn.graph.succ(a))
592 if (is_act(s)) succ_act[a].push_back(s);
600 std::vector<std::vector<double> > or_prob(nidx + 1);
601 for (std::size_t a = a_lo; a <= a_hi; ++a) {
602 const std::vector<std::size_t>& sc = succ_act[a];
603 if (sc.size() < 2)
continue;
606 for (std::size_t s : sc) {
607 const double w = wgt(a, s);
608 if (w != 1.0 && w > 0.0) prob =
true;
612 for (std::size_t s : sc) tot += wgt(a, s);
613 for (std::size_t s : sc) {
614 const double w = wgt(a, s);
615 or_prob[a].push_back((tot > 0.0 && tot != 1.0) ? w / tot : w);
620 std::vector<std::size_t> join_required(nidx + 1, 0);
621 for (std::size_t a = a_lo; a <= a_hi; ++a)
623 ++join_required[succ_act[a][0]];
627 for (std::size_t a = a_lo; a <= a_hi; ++a) {
628 const std::size_t q = a <
lsn.actquorum.size() ?
lsn.actquorum[a] : 0;
629 if (q > 0 && join_required[a] > 0 && q < join_required[a])
630 throw UnsupportedError(
"SolverLDES (native LN engine): the AND join into '" +
631 lsn.names[a] +
"' declares a quorum of " + std::to_string(q) +
632 " of " + std::to_string(join_required[a]) +
633 " inputs; LDES joins wait for every input");
640 std::vector<std::size_t> bound(nidx + 1, 0);
641 for (std::size_t e =
lsn.eshift + 1; e <=
lsn.eshift +
lsn.nentries; ++e) {
642 const std::size_t t =
lsn.parent[e];
643 const std::vector<std::size_t>& acts =
644 (t <
lsn.actsof.size()) ?
lsn.actsof[t] : std::vector<std::size_t>();
645 for (std::size_t a : acts)
646 if (wgt(e, a) > 0.0) {
650 if (bound[e] != 0 || acts.empty())
continue;
651 for (std::size_t a : acts) {
654 bool haspred =
false;
655 for (std::size_t o2 : acts)
656 if (o2 != a && wgt(o2, a) > 0.0) {
665 if (bound[e] == 0) bound[e] = acts[0];
669 std::vector<LnCacheState> caches;
670 std::vector<std::size_t> cache_of_slot(ntask_slots, LN_NONE);
671 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
672 if (t >=
lsn.iscache.size() || !
lsn.iscache[t])
continue;
673 const std::size_t n = t <
lsn.nitems.size() ?
lsn.nitems[t] : 0;
674 if (n == 0)
continue;
675 std::vector<int> cap = (t <
lsn.itemcap.size()) ?
lsn.itemcap[t] : std::vector<int>();
676 if (cap.empty()) cap.assign(1, 1);
677 for (std::size_t m = 0; m < repl[t]; ++m) {
681 cs.rs = (t <
lsn.replacestrat.size()) ?
lsn.replacestrat[t]
683 cs.levels.assign(cap.size(), std::list<std::size_t>());
684 cs.retrieval = t <
lsn.hasretrieval.size() &&
lsn.hasretrieval[t];
686 cs.inflight.assign(n, 0);
687 cs.held.assign(n, {});
689 cache_of_slot[task_slot(t, m)] = caches.size();
690 caches.push_back(cs);
693 std::vector<LnCacheAccess> accesses;
694 std::vector<std::size_t> access_of_driver(nidx + 1, LN_NONE);
695 std::vector<std::size_t> branch_reply(nidx + 1, 0);
696 for (std::size_t e =
lsn.eshift + 1; e <=
lsn.eshift +
lsn.nentries; ++e) {
697 const std::size_t t =
lsn.parent[e];
698 if (t <= lsn.tshift || t >
lsn.tshift +
lsn.ntasks)
continue;
699 if (cache_of_slot[task_slot(t, 0)] == LN_NONE)
continue;
700 const std::size_t drv = bound[e];
701 const bool is_item = e <
lsn.itemproc.size() && !
lsn.itemproc[e].empty();
704 if (!is_item)
continue;
705 if (drv == 0 || succ_act[drv].size() < 2)
707 "' has no cache access with a hit and a miss branch");
709 for (std::size_t m = 0; m < repl[t]; ++m) ca.caches.push_back(cache_of_slot[task_slot(t, m)]);
710 ca.hit = succ_act[drv][0];
711 ca.miss = succ_act[drv][1];
713 std::size_t card = e <
lsn.nitems.size() ?
lsn.nitems[e] : 0;
714 if (card == 0) card = caches[ca.caches[0]].nitems;
716 for (std::size_t i = 0; i < card; ++i) {
717 const double v = (i <
lsn.itemproc[e].size())
719 if (std::isnan(v) || v < 0.0)
720 throw InputError(
"SolverLDES (native LN engine): ItemEntry '" +
lsn.names[e] +
721 "' has a popularity that is not a pmf");
723 ca.cdf.push_back(
sum);
726 throw InputError(
"SolverLDES (native LN engine): ItemEntry '" +
lsn.names[e] +
727 "' has a popularity of zero total mass");
728 for (
double& c : ca.cdf) c /=
sum;
729 access_of_driver[drv] = accesses.size();
730 branch_reply[ca.hit] = e;
731 branch_reply[ca.miss] = e;
732 accesses.push_back(ca);
736 for (std::size_t a = a_lo; a <= a_hi; ++a) {
737 if (access_of_driver[a] != LN_NONE)
continue;
738 for (std::size_t s : succ_act[a])
741 "' reads a cache, but no ItemEntry of a cache task with "
742 "a popularity is bound to it");
752 std::vector<std::vector<double> > A;
753 std::vector<double> b;
754 std::map<std::size_t, int> col;
756 std::vector<Lincon> lincon(nidx + 1);
757 std::vector<bool> has_lincon(nidx + 1,
false);
758 for (std::size_t k = 1; k <
lsn.lincon_A.size() && k <= nidx; ++k) {
760 if (A.
rows() == 0)
continue;
761 const bool host = k >
lsn.hshift && k <=
lsn.hshift +
lsn.nhosts;
762 const std::vector<std::size_t>* ops =
nullptr;
763 if (host && k <
lsn.tasksof.size()) ops = &
lsn.tasksof[k];
764 if (!host && k <
lsn.entriesof.size()) ops = &
lsn.entriesof[k];
765 const std::size_t ncols = ops ? ops->size() : 0;
766 if (A.
cols() != ncols)
767 throw InputError(
"SolverLDES (native LN engine): the admission constraint on '" +
768 lsn.names[k] +
"' has " + std::to_string(A.
cols()) +
769 " columns but the server has " + std::to_string(ncols) +
" operands");
770 if (ncols == 0)
continue;
772 lc.A.assign(A.
rows(), std::vector<double>(ncols, 0.0));
773 for (std::size_t r = 0; r < A.
rows(); ++r)
775 for (std::size_t r = 0; r < A.
rows(); ++r)
776 lc.b.push_back((k <
lsn.lincon_b.size() && r <
lsn.lincon_b[k].size())
778 : std::numeric_limits<double>::infinity());
779 for (std::size_t c = 0; c < ncols; ++c) lc.col[(*ops)[c]] =
static_cast<int>(c);
781 has_lincon[k] =
true;
783 auto lincon_col = [&](std::size_t elem, std::size_t operand) ->
int {
784 if (!has_lincon[elem])
return -1;
785 const std::map<std::size_t, int>::const_iterator it = lincon[elem].col.find(operand);
786 return it == lincon[elem].col.end() ? -1 : it->second;
789 auto admits = [&](std::size_t elem,
const std::vector<int>& occ,
int col) {
790 const Lincon& lc = lincon[elem];
791 for (std::size_t r = 0; r < lc.A.size(); ++r) {
792 double lhs = lc.A[r][
static_cast<std::size_t
>(col)];
793 for (std::size_t k = 0; k < occ.size(); ++k) lhs += lc.A[r][k] * occ[k];
794 if (lhs > lc.b[r] + 1e-9)
return false;
805 std::vector<bool> pooled(nidx + 1,
false);
806 std::vector<std::vector<double> > pool_capacity(nidx + 1);
807 std::vector<std::vector<std::vector<bool> > > pool_serves(nidx + 1);
808 std::vector<std::map<std::size_t, int> > pool_col(nidx + 1);
809 for (std::size_t h =
lsn.hshift + 1; h <=
lsn.hshift +
lsn.nhosts; ++h) {
810 if (h >=
lsn.pools.size() ||
lsn.pools[h].npools() == 0)
continue;
811 if (host_sched[h] == SchedStrategy::INF)
continue;
814 std::size_t declared = 0;
815 for (std::size_t p = 0; p < np; ++p) {
816 const double cnt = (p < P.
counts.size()) ? P.
counts[p] : 0.0;
817 const std::size_t c = cnt > 0.0 ?
static_cast<std::size_t
>(cnt) : 0;
819 pool_capacity[h].push_back(
static_cast<double>(c) * (r > 0.0 ? r : 1.0));
821 std::vector<bool> sv(
nc,
false);
822 for (std::size_t j = 0; j <
nc; ++j)
824 pool_serves[h].push_back(sv);
826 if (declared != host_servers[h])
827 throw InputError(
"SolverLDES (native LN engine): processor '" +
lsn.names[h] +
828 "' declares server pools holding " + std::to_string(declared) +
829 " servers but has multiplicity " +
830 (host_servers[h] == std::numeric_limits<std::size_t>::max()
831 ? std::string(
"Inf") : std::to_string(host_servers[h])) +
832 "; the pools must partition the servers");
833 const std::vector<std::size_t>& ops =
834 (h <
lsn.tasksof.size()) ?
lsn.tasksof[h] : std::vector<std::size_t>();
835 for (std::size_t j = 0; j < ops.size() && j <
nc; ++j) pool_col[h][ops[j]] =
static_cast<int>(j);
841 std::priority_queue<LnEvent, std::vector<LnEvent>, LnEventLater> evq;
842 std::uint64_t seq = 0;
845 std::deque<LnLine> lines;
846 std::deque<LnInv> invs;
847 std::vector<std::size_t> free_lines, free_invs;
848 std::vector<LnCustomer> custs;
851 std::vector<std::size_t> host_busy(nhost_slots, 0);
852 std::vector<std::deque<std::size_t>> host_queue(nhost_slots);
854 std::vector<std::vector<std::size_t> > pool_jobs(nhost_slots);
855 std::vector<double> pool_last(nhost_slots, 0.0), pool_rate(nhost_slots, 0.0);
856 std::vector<std::uint64_t> pool_gen(nhost_slots, 0);
857 std::vector<std::size_t> host_of_slot(nhost_slots, 0);
858 for (std::size_t h =
lsn.hshift + 1; h <=
lsn.hshift +
lsn.nhosts; ++h)
859 for (std::size_t m = 0; m < repl[h]; ++m) host_of_slot[host_slot0[h] + m] = h;
861 std::vector<std::vector<int> > hocc(nhost_slots), tocc(ntask_slots);
862 std::vector<std::deque<std::size_t> > hadm(nhost_slots), tadm(ntask_slots);
863 for (std::size_t h =
lsn.hshift + 1; h <=
lsn.hshift +
lsn.nhosts; ++h)
865 for (std::size_t m = 0; m < repl[h]; ++m)
866 hocc[host_slot0[h] + m].assign(lincon[h].A[0].size(), 0);
870 std::vector<std::vector<bool>> thr_held(ntask_slots);
871 std::vector<std::vector<ThreadState>> thr_state(ntask_slots);
872 std::vector<std::vector<double>> thr_setup_t0(ntask_slots);
873 std::vector<std::vector<std::uint64_t>> thr_gen(ntask_slots);
874 std::vector<std::deque<std::size_t>> thr_queue(ntask_slots);
875 std::vector<std::size_t> thr_owner(ntask_slots, 0);
876 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
877 const std::size_t n = (task_threads[t] == std::numeric_limits<std::size_t>::max())
878 ? 0 : task_threads[t];
879 for (std::size_t m = 0; m < repl[t]; ++m) {
880 const std::size_t ts = task_slot(t, m);
882 thr_held[ts].assign(n,
false);
883 thr_state[ts].assign(n, ThreadState::ACTIVE);
884 thr_setup_t0[ts].assign(n, 0.0);
885 thr_gen[ts].assign(n, 0);
886 if (has_lincon[t]) tocc[ts].assign(lincon[t].A[0].size(), 0);
893 std::vector<Sampler> setupd(nidx + 1), delayoffd(nidx + 1);
894 std::vector<bool> has_setup(nidx + 1,
false);
895 std::vector<Rng> g_setup, g_doff;
896 g_setup.reserve(nidx + 1);
897 g_doff.reserve(nidx + 1);
898 for (std::size_t k = 0; k <= nidx; ++k) {
899 g_setup.push_back(Rng(ln_seed,
static_cast<long long>(k) * 10 + 2000));
900 g_doff.push_back(Rng(ln_seed,
static_cast<long long>(k) * 10 + 3000));
902 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
903 if (t >=
lsn.hassetup.size() || !
lsn.hassetup[t])
continue;
904 const bool su = t <
lsn.setuptime.size() && !
lsn.setuptime[t].disabled &&
906 const bool df = t <
lsn.delayofftime.size() && !
lsn.delayofftime[t].disabled &&
908 if (!su || !df)
continue;
909 setupd[t] = Sampler(
lsn.setuptime[t],
"the setup time of '" +
lsn.names[t] +
"'");
910 delayoffd[t] = Sampler(
lsn.delayofftime[t],
"the delay-off time of '" +
lsn.names[t] +
"'");
921 std::vector<Rng> g_route, g_item, g_rr;
922 g_route.reserve(nidx + 1);
923 for (std::size_t k = 0; k <= nidx; ++k)
924 g_route.push_back(Rng(ln_seed, 940000 +
static_cast<long long>(k) * 10));
925 for (std::size_t c = 0; c < caches.size(); ++c) {
926 g_item.push_back(Rng(ln_seed, 950000 +
static_cast<long long>(c) * 10));
927 g_rr.push_back(Rng(ln_seed, 960000 +
static_cast<long long>(c) * 10));
932 std::deque<std::size_t> runnable;
942 auto task_prio = [&](std::size_t task) ->
int {
943 return (task <
lsn.prio.size()) ?
lsn.prio[task] : 0;
945 auto host_req_prio = [&](std::size_t lid) ->
int {
return task_prio(
lsn.parent[lines[lid].act]); };
946 auto task_req_prio = [&](std::size_t vi) ->
int {
947 const std::size_t c = invs[vi].caller;
948 return c == LN_NONE ? 0 : task_prio(invs[lines[c].inv].task);
952 Rng g_siro_host(ln_seed, 910000), g_siro_task(ln_seed, 920000);
953 auto fcfs_prio = [](SchedStrategy sc) {
955 return sc == SchedStrategy::HOL || sc == SchedStrategy::FCFSPRPRIO ||
956 sc == SchedStrategy::FCFSPIPRIO;
958 auto lcfs_prio = [](SchedStrategy sc) {
959 return sc == SchedStrategy::LCFSPRIO || sc == SchedStrategy::LCFSPRPRIO ||
960 sc == SchedStrategy::LCFSPIPRIO;
962 auto preemptive = [](SchedStrategy sc) {
963 return sc == SchedStrategy::FCFSPRPRIO || sc == SchedStrategy::FCFSPIPRIO ||
964 sc == SchedStrategy::LCFSPRPRIO || sc == SchedStrategy::LCFSPIPRIO;
966 auto is_ps = [](SchedStrategy sc) {
967 return sc == SchedStrategy::PS || sc == SchedStrategy::PSPRIO;
974 auto prio_enqueue = [](std::deque<std::size_t>& q, std::size_t id,
975 const std::function<int(std::size_t)>& prio_of,
bool lifo,
976 const std::function<double(std::size_t)>* key_of) {
977 const int prio = prio_of(
id);
978 const double key = key_of ? (*key_of)(id) : 0.0;
979 std::deque<std::size_t>::iterator it = q.end();
980 while (it != q.begin()) {
981 const std::size_t o = *(it - 1);
982 const int po = prio_of(o);
983 if (po > prio)
break;
985 if (!lifo && (!key_of || (*key_of)(o) <= key))
break;
986 if (lifo && key_of && (*key_of)(o) > key)
break;
997 auto enqueue = [&](std::deque<std::size_t>& q, std::size_t id, SchedStrategy sc,
998 const std::function<int(std::size_t)>& prio_of,
double& key, Rng& g_siro,
999 const std::function<double(std::size_t)>* key_of) {
1000 if (fcfs_prio(sc) || lcfs_prio(sc)) {
1001 prio_enqueue(q,
id, prio_of, lcfs_prio(sc), key_of);
1002 }
else if (sc == SchedStrategy::LCFS) {
1005 if (sc == SchedStrategy::SIRO) key = uniform01(g_siro);
1010 auto dequeue = [&](std::deque<std::size_t>& q, SchedStrategy sc,
1011 const std::function<double(std::size_t)>& key_of) -> std::size_t {
1012 std::deque<std::size_t>::iterator best = q.begin();
1013 if (sc == SchedStrategy::SIRO)
1014 for (std::deque<std::size_t>::iterator it = q.begin(); it != q.end(); ++it)
1015 if (key_of(*it) < key_of(*best)) best = it;
1016 const std::size_t
id = *best;
1020 const std::function<double(std::size_t)> line_key = [&](std::size_t lid) {
1021 return lines[lid].siro_key;
1023 const std::function<double(std::size_t)> inv_key = [&](std::size_t vi) {
1024 return invs[vi].siro_key;
1026 const std::function<double(std::size_t)> host_key = [&](std::size_t lid) {
1027 return lines[lid].host_t0;
1036 std::vector<std::vector<std::size_t> > ps_jobs(nhost_slots);
1037 std::vector<double> ps_last(nhost_slots, 0.0);
1038 std::vector<std::uint64_t> ps_gen(nhost_slots, 0);
1044 auto ps_rates = [&](std::size_t hs) -> std::vector<double> {
1045 const std::vector<std::size_t>& pj = ps_jobs[hs];
1046 const std::size_t n = pj.size();
1047 const std::size_t host = host_of_slot[hs];
1048 const double c =
static_cast<double>(host_servers[host]);
1049 if (host_sched[host] != SchedStrategy::PSPRIO ||
static_cast<double>(n) <= c)
1050 return std::vector<double>(n, n == 0 ? 0.0 : std::min(1.0, c /
static_cast<double>(n)));
1051 std::vector<double> rate(n, 0.0);
1052 std::vector<int> pr(n);
1053 for (std::size_t i = 0; i < n; ++i) pr[i] = task_prio(
lsn.parent[lines[pj[i]].act]);
1054 std::vector<int> levels(pr);
1055 std::sort(levels.begin(), levels.end(), std::greater<int>());
1056 levels.erase(std::unique(levels.begin(), levels.end()), levels.end());
1058 for (std::size_t l = 0; l < levels.size() && left > 0.0; ++l) {
1059 std::size_t cnt = 0;
1060 for (std::size_t i = 0; i < n; ++i) cnt += (pr[i] == levels[l]) ? 1 : 0;
1061 const double alloc = std::min(left,
static_cast<double>(cnt));
1062 for (std::size_t i = 0; i < n; ++i)
1063 if (pr[i] == levels[l]) rate[i] = alloc /
static_cast<double>(cnt);
1069 auto ps_advance = [&](std::size_t hs) {
1070 const double dt = now - ps_last[hs];
1072 const std::vector<double> rate = ps_rates(hs);
1073 for (std::size_t i = 0; i < ps_jobs[hs].size(); ++i) {
1074 LnLine& L = lines[ps_jobs[hs][i]];
1075 L.remaining = std::max(0.0, L.remaining - dt * rate[i]);
1081 auto ps_busy = [&](std::size_t hs, std::size_t n) ->
double {
1082 return static_cast<double>(std::min(n, host_servers[host_of_slot[hs]]));
1086 std::vector<double> tot_q(nidx + 1, 0.0), tot_u(nidx + 1, 0.0);
1087 std::vector<double> cur_q(nidx + 1, 0.0), cur_u(nidx + 1, 0.0);
1088 std::vector<double> last_upd(nidx + 1, 0.0);
1089 std::vector<double> completions(nidx + 1, 0.0), resp_sum(nidx + 1, 0.0),
1090 resp_cnt(nidx + 1, 0.0);
1094 std::vector<std::vector<double> > entry_resp_samples(
lsn.nentries);
1102 std::vector<double> act_host_resid(nidx + 1, 0.0);
1107 std::vector<Rng> g_callmult;
1108 g_callmult.reserve(
lsn.ncalls + 1);
1109 for (std::size_t c = 0; c <=
lsn.ncalls; ++c)
1110 g_callmult.push_back(Rng(ln_seed,
static_cast<long long>(c) * 10 + 4000));
1120 auto sample_call_count = [&](std::size_t cidx) -> std::size_t {
1121 const double m = (cidx <
lsn.callproc_mean.size())
1124 if (!(m > 0.0))
return 0;
1125 std::size_t n =
static_cast<std::size_t
>(std::floor(m));
1126 const double frac = m -
static_cast<double>(n);
1127 if (frac > 0.0 && cidx < g_callmult.size() && uniform01(g_callmult[cidx]) < frac) ++n;
1131 auto touch = [&](std::size_t k) {
1132 const double dt = now - last_upd[k];
1134 tot_q[k] += cur_q[k] * dt;
1135 tot_u[k] += cur_u[k] * dt;
1139 auto push_ev = [&](LnEvent e) {
1144 std::uint64_t done = 0;
1147 auto new_line = [&](std::size_t inv, std::size_t act,
bool branch) -> std::size_t {
1149 if (!free_lines.empty()) {
1150 id = free_lines.back();
1151 free_lines.pop_back();
1152 lines[id] = LnLine();
1155 lines.push_back(LnLine());
1157 lines[id].inv = inv;
1158 lines[id].act = act;
1159 lines[id].calls_left = NOTDRAWN;
1160 lines[id].branch = branch;
1163 auto free_line = [&](std::size_t id) { free_lines.push_back(
id); };
1165 auto set_act = [&](std::size_t lid, std::size_t a) {
1166 LnLine& L = lines[lid];
1170 L.calls_left = NOTDRAWN;
1173 auto new_inv =[&](std::size_t entry, std::size_t trep, std::size_t caller,
1174 std::size_t cust) -> std::size_t {
1176 if (!free_invs.empty()) {
1177 id = free_invs.back();
1178 free_invs.pop_back();
1182 invs.push_back(LnInv());
1184 LnInv& v = invs[id];
1186 v.task =
lsn.parent[entry];
1188 v.ts = task_slot(v.task, trep);
1192 const std::size_t root = new_line(
id, bound[entry],
false);
1193 invs[id].root = root;
1201 auto enter_task = [&](std::size_t vi) {
1202 LnInv& v = invs[vi];
1204 cur_q[v.task] += 1.0;
1211 auto arrive_at_task = [&](std::size_t vi) ->
bool {
1212 LnInv& v = invs[vi];
1213 const int col = lincon_col(v.task, v.entry);
1216 if (!admits(v.task, tocc[v.ts], col)) {
1217 tadm[v.ts].push_back(vi);
1220 ++tocc[v.ts][
static_cast<std::size_t
>(col)];
1227 std::function<void(std::size_t)> advance;
1229 auto drain = [&]() {
1230 while (!runnable.empty()) {
1231 const std::size_t
id = runnable.front();
1232 runnable.pop_front();
1238 std::function<void(std::size_t)> advance_pool;
1239 auto pool_rates = [&](std::size_t hs) {
1240 const std::size_t h = host_of_slot[hs];
1241 const std::vector<std::size_t>& jobs = pool_jobs[hs];
1242 std::vector<double> rate(jobs.size(), 0.0);
1243 for (std::size_t p = 0; p < pool_capacity[h].size(); ++p)
1244 for (std::size_t i = 0; i < jobs.size(); ++i) {
1245 const int j = lines[jobs[i]].pcol;
1246 if (j >= 0 &&
static_cast<std::size_t
>(j) < pool_serves[h][p].size() &&
1247 pool_serves[h][p][
static_cast<std::size_t
>(j)]) {
1248 rate[i] += pool_capacity[h][p];
1255 advance_pool = [&](std::size_t hs) {
1256 const std::vector<double> r = pool_rates(hs);
1257 const double dt = now - pool_last[hs];
1258 for (std::size_t i = 0; i < pool_jobs[hs].size(); ++i) {
1259 LnLine& L = lines[pool_jobs[hs][i]];
1260 L.remaining = std::max(0.0, L.remaining - dt * r[i]);
1262 pool_last[hs] = now;
1269 auto rearm_pool = [&](std::size_t hs) {
1270 const std::size_t h = host_of_slot[hs];
1271 const std::vector<double> r = pool_rates(hs);
1272 double tot = 0.0, tmin = std::numeric_limits<double>::infinity();
1273 for (std::size_t i = 0; i < r.size(); ++i) {
1275 if (r[i] > 0.0) tmin = std::min(tmin, lines[pool_jobs[hs][i]].remaining / r[i]);
1278 pool_rate[hs] = tot;
1280 for (std::size_t m = 0; m < repl[h]; ++m) u += pool_rate[host_slot0[h] + m];
1282 const std::uint64_t g = ++pool_gen[hs];
1283 if (std::isfinite(tmin)) {
1293 auto ps_schedule = [&](std::size_t hs) {
1295 if (ps_jobs[hs].empty())
return;
1296 const std::vector<double> rate = ps_rates(hs);
1297 double tmin = std::numeric_limits<double>::infinity();
1298 for (std::size_t i = 0; i < ps_jobs[hs].size(); ++i)
1299 if (rate[i] > 0.0) tmin = std::min(tmin, lines[ps_jobs[hs][i]].remaining / rate[i]);
1309 std::vector<std::vector<std::size_t> > in_service(nhost_slots);
1310 std::uint64_t host_gen_ctr = 0;
1315 auto begin_host = [&](std::size_t lid, std::size_t hs) {
1316 LnLine& L = lines[lid];
1317 if (L.host_rem < 0.0) L.host_rem = hostdem[L.act].next(g_host[L.act]);
1319 L.host_end = now + L.host_rem;
1320 if (preemptive(host_sched[host_of_slot[hs]])) in_service[hs].push_back(lid);
1321 L.host_gen = ++host_gen_ctr;
1336 auto preempt_for = [&](std::size_t lid, std::size_t hs) ->
bool {
1337 const SchedStrategy sc = host_sched[host_of_slot[hs]];
1338 const bool lifo = lcfs_prio(sc);
1339 const int pa = host_req_prio(lid);
1340 std::vector<std::size_t>& ins = in_service[hs];
1341 std::size_t vi = ins.size();
1343 for (std::size_t i = 0; i < ins.size(); ++i) {
1344 const int pr = host_req_prio(ins[i]);
1345 if (pr >= pa)
continue;
1346 bool better = vi == ins.size() || pr < pv;
1347 if (!better && pr == pv) {
1348 const LnLine& r = lines[ins[i]];
1349 const LnLine& v = lines[ins[vi]];
1350 better = lifo ? r.host_t0 < v.host_t0 : r.host_start > v.host_start;
1357 if (vi == ins.size() && lifo)
1358 for (std::size_t i = 0; i < ins.size(); ++i)
1359 if (host_req_prio(ins[i]) == pa &&
1360 (vi == ins.size() || lines[ins[i]].host_start < lines[ins[vi]].host_start))
1362 if (vi == ins.size())
return false;
1363 const std::size_t victim = ins[vi];
1364 ins.erase(ins.begin() +
static_cast<std::ptrdiff_t
>(vi));
1365 LnLine& v = lines[victim];
1366 v.host_gen = ++host_gen_ctr;
1367 const bool resume = sc == SchedStrategy::FCFSPRPRIO || sc == SchedStrategy::LCFSPRPRIO;
1368 v.host_rem = resume ? std::max(0.0, v.host_end - now) : -1.0;
1369 enqueue(host_queue[hs], victim, sc, host_req_prio, v.siro_key, g_siro_host, &host_key);
1370 begin_host(lid, hs);
1374 auto start_host_service = [&](std::size_t lid) {
1375 LnLine& L = lines[lid];
1376 const std::size_t act = L.act;
1377 const std::size_t host =
lsn.host_of(act);
1378 const std::size_t hs = host_slot(host, invs[L.inv].trep);
1383 const std::size_t t =
lsn.parent[act];
1384 const std::map<std::size_t, int>::const_iterator it = pool_col[host].find(t);
1385 if (it == pool_col[host].end())
1386 throw InputError(
"SolverLDES (native LN engine): activity '" +
lsn.names[act] +
1387 "' runs on a host whose pool declaration does not name its "
1388 "task, so no server can be assigned");
1389 lines[lid].pcol = it->second;
1390 lines[lid].remaining = hostdem[act].next(g_host[act]);
1391 pool_jobs[hs].push_back(lid);
1395 lines[lid].host_rem = -1.0;
1396 if (is_ps(host_sched[host])) {
1399 const std::size_t n0 = ps_jobs[hs].size();
1400 lines[lid].remaining = hostdem[act].next(g_host[act]);
1401 ps_jobs[hs].push_back(lid);
1403 cur_u[host] += ps_busy(hs, n0 + 1) - ps_busy(hs, n0);
1407 if (host_busy[hs] < host_servers[host]) {
1410 begin_host(lid, hs);
1411 }
else if (!(preemptive(host_sched[host]) && preempt_for(lid, hs))) {
1412 enqueue(host_queue[hs], lid, host_sched[host], host_req_prio, lines[lid].siro_key,
1413 g_siro_host, &host_key);
1422 auto request_host = [&](std::size_t lid) {
1423 LnLine& L = lines[lid];
1424 const std::size_t act = L.act;
1425 const std::size_t host =
lsn.host_of(act);
1426 const std::size_t hs = host_slot(host, invs[L.inv].trep);
1428 const int col = lincon_col(host,
lsn.parent[act]);
1431 if (!admits(host, hocc[hs], col)) {
1432 hadm[hs].push_back(lid);
1435 ++hocc[hs][
static_cast<std::size_t
>(col)];
1437 start_host_service(lid);
1440 auto release_host_admission = [&](std::size_t lid) {
1441 LnLine& L = lines[lid];
1442 if (L.hcol < 0)
return;
1443 const std::size_t host =
lsn.host_of(L.act);
1444 const std::size_t hs = host_slot(host, invs[L.inv].trep);
1445 --hocc[hs][
static_cast<std::size_t
>(L.hcol)];
1447 while (!hadm[hs].empty()) {
1448 const std::size_t head = hadm[hs].front();
1449 const int c = lines[head].hcol;
1450 if (!admits(host, hocc[hs], c))
return;
1451 hadm[hs].pop_front();
1452 ++hocc[hs][
static_cast<std::size_t
>(c)];
1453 start_host_service(head);
1458 auto start_setup = [&](std::size_t ts, std::size_t th) {
1459 const std::size_t task = thr_owner[ts];
1460 thr_state[ts][th] = ThreadState::SETUP;
1461 thr_setup_t0[ts][th] = now;
1463 e.t = now + setupd[task].next(g_setup[task]);
1467 e.gen = ++thr_gen[ts][th];
1472 auto start_delayoff = [&](std::size_t ts, std::size_t th) {
1473 const std::size_t task = thr_owner[ts];
1474 thr_state[ts][th] = ThreadState::DELAYOFF;
1476 e.t = now + delayoffd[task].next(g_doff[task]);
1480 e.gen = ++thr_gen[ts][th];
1493 auto acquire_thread = [&](std::size_t vi) ->
bool {
1494 LnInv& v = invs[vi];
1495 const std::size_t task = v.task;
1496 const std::size_t ts = v.ts;
1498 if (task_threads[task] == std::numeric_limits<std::size_t>::max()) {
1500 v.has_thread =
true;
1504 std::vector<bool>& held = thr_held[ts];
1505 std::vector<ThreadState>& st = thr_state[ts];
1509 for (std::size_t th = 0; th < held.size(); ++th) {
1510 if (!held[th] && st[th] == ThreadState::ACTIVE) {
1513 v.has_thread =
true;
1518 for (std::size_t th = 0; th < held.size(); ++th) {
1519 if (!held[th] && st[th] == ThreadState::DELAYOFF) {
1521 st[th] = ThreadState::ACTIVE;
1524 v.has_thread =
true;
1529 enqueue(thr_queue[ts], vi, task_sched[task], task_req_prio, invs[vi].siro_key, g_siro_task,
1531 for (std::size_t th = 0; th < held.size(); ++th) {
1532 if (!held[th] && st[th] == ThreadState::OFF) {
1533 start_setup(ts, th);
1541 auto release_thread = [&](std::size_t vi) {
1542 LnInv& v = invs[vi];
1543 const std::size_t ts = v.ts;
1544 const std::size_t th = v.th;
1545 const std::size_t task = thr_owner[ts];
1548 v.has_thread =
false;
1550 if (th == NOTHR)
return;
1551 thr_held[ts][th] =
false;
1552 if (!thr_queue[ts].empty()) {
1553 const std::size_t nxt = dequeue(thr_queue[ts], task_sched[task], inv_key);
1554 thr_held[ts][th] =
true;
1556 invs[nxt].has_thread =
true;
1558 runnable.push_back(invs[nxt].root);
1559 }
else if (has_setup[task]) {
1560 start_delayoff(ts, th);
1575 auto finish_act = [&](LnLine& L) {
1576 const std::size_t act = L.act;
1577 completions[act] += 1.0;
1578 if (L.act_t0 >= 0.0) {
1579 resp_sum[act] += now - L.act_t0;
1580 resp_cnt[act] += 1.0;
1598 auto send_reply = [&](std::size_t vi,
bool direct) {
1599 LnInv& v = invs[vi];
1600 if (v.replied)
return;
1602 v.early_reply = !direct;
1603 completions[v.entry] += 1.0;
1604 const double r = now - v.entry_t0;
1605 resp_sum[v.entry] += r;
1606 resp_cnt[v.entry] += 1.0;
1610 if (v.entry >
lsn.eshift && v.entry <=
lsn.eshift +
lsn.nentries)
1611 entry_resp_samples[v.entry -
lsn.eshift - 1].push_back(r);
1612 if (!direct && v.caller != LN_NONE) runnable.push_back(v.caller);
1617 auto cache_insert = [&](std::size_t ci, std::size_t item) {
1618 LnCacheState& cs = caches[ci];
1619 std::list<std::size_t>& lst = cs.levels[0];
1620 const std::size_t cap =
static_cast<std::size_t
>(std::max(0, cs.cap[0]));
1622 if (lst.size() >= cap && !lst.empty()) {
1623 std::size_t r =
static_cast<std::size_t
>(uniform01(g_rr[ci]) *
1624 static_cast<double>(lst.size()));
1625 r = std::min(r, lst.size() - 1);
1626 std::list<std::size_t>::iterator it = lst.begin();
1627 std::advance(it,
static_cast<long>(r));
1630 lst.push_front(item);
1633 lst.push_front(item);
1634 if (lst.size() > cap) lst.pop_back();
1638 auto cache_promote = [&](std::size_t ci, std::size_t item, std::size_t level) {
1639 LnCacheState& cs = caches[ci];
1641 const std::size_t inew = std::min(level + 1, cs.levels.size() - 1);
1645 if (inew <= level) {
1647 cs.levels[level].remove(item);
1648 cs.levels[level].push_front(item);
1652 std::list<std::size_t>& lo = cs.levels[level];
1653 std::list<std::size_t>& hi = cs.levels[inew];
1654 std::size_t kpos = 0;
1655 for (std::list<std::size_t>::iterator it = lo.begin(); it != lo.end(); ++it, ++kpos)
1656 if (*it == item)
break;
1658 if (hi.size() <
static_cast<std::size_t
>(std::max(0, cs.cap[inew]))) {
1659 hi.push_front(item);
1662 std::size_t demoted;
1664 std::size_t r =
static_cast<std::size_t
>(uniform01(g_rr[ci]) *
1665 static_cast<double>(hi.size()));
1666 r = std::min(r, hi.size() - 1);
1667 std::list<std::size_t>::iterator it = hi.begin();
1668 std::advance(it,
static_cast<long>(r));
1672 demoted = hi.back();
1674 hi.push_front(item);
1677 lo.push_front(demoted);
1679 std::list<std::size_t>::iterator it = lo.begin();
1680 std::advance(it,
static_cast<long>(std::min(kpos, lo.size())));
1681 lo.insert(it, demoted);
1684 static const std::size_t HELD =
static_cast<std::size_t
>(-2);
1691 auto access_cache = [&](std::size_t ai, std::size_t lid) -> std::size_t {
1692 const LnCacheAccess& ca = accesses[ai];
1693 LnInv& v = invs[lines[lid].inv];
1694 const std::size_t ci = ca.caches[std::min(v.trep, ca.caches.size() - 1)];
1695 LnCacheState& cs = caches[ci];
1697 const double u = uniform01(g_item[ci]);
1698 std::size_t item = ca.cdf.size() - 1;
1699 for (std::size_t i = 0; i < ca.cdf.size(); ++i)
1700 if (u <= ca.cdf[i]) {
1704 for (std::size_t l = 0; l < cs.levels.size(); ++l)
1705 if (std::find(cs.levels[l].begin(), cs.levels[l].end(), item) != cs.levels[l].end()) {
1707 cache_promote(ci, item, l);
1711 if (item < cs.inflight.size() && cs.inflight[item]) {
1712 cs.held[item].push_back(std::make_pair(lid, ai));
1716 cs.inflight[item] = 1;
1719 v.fetch_item = item;
1723 cache_insert(ci, item);
1727 auto release_fetch = [&](std::size_t vi) {
1728 LnInv& v = invs[vi];
1729 if (!v.fetching)
return;
1731 LnCacheState& cs = caches[v.fetch_cache];
1732 const std::size_t item = v.fetch_item;
1733 cs.inflight[item] = 0;
1734 cache_insert(v.fetch_cache, item);
1735 std::vector<std::pair<std::size_t, std::size_t> > held;
1736 held.swap(cs.held[item]);
1737 for (std::size_t k = 0; k < held.size(); ++k) {
1738 ++caches[v.fetch_cache].delayed;
1739 set_act(held[k].first, accesses[held[k].second].hit);
1740 runnable.push_back(held[k].first);
1751 auto complete_request = [&](std::size_t vi) -> std::size_t {
1752 send_reply(vi,
true);
1754 LnInv& v = invs[vi];
1756 cur_q[v.entry] -= 1.0;
1757 completions[v.task] += 1.0;
1759 cur_q[v.task] -= 1.0;
1762 const std::size_t ts = v.ts, task = v.task;
1763 --tocc[ts][
static_cast<std::size_t
>(v.tcol)];
1765 while (!tadm[ts].empty()) {
1766 const std::size_t head = tadm[ts].front();
1767 const int c = invs[head].tcol;
1768 if (!admits(task, tocc[ts], c))
break;
1769 tadm[ts].pop_front();
1770 ++tocc[ts][
static_cast<std::size_t
>(c)];
1772 runnable.push_back(invs[head].root);
1775 LnInv& w = invs[vi];
1776 const std::size_t caller = w.caller, cust = w.cust;
1777 const bool early = w.early_reply;
1779 free_invs.push_back(vi);
1780 if (cust != LN_NONE) {
1784 const std::size_t rt = custs[cust].ref_task;
1785 resp_sum[rt] += now - custs[cust].t_start;
1786 resp_cnt[rt] += 1.0;
1789 e.t = now + (has_think[rt] ? think[rt].next(g_think[rt]) : 0.0);
1795 return (caller != LN_NONE && !early) ? caller : LN_NONE;
1799 auto finish_line = [&](std::size_t lid) -> std::size_t {
1800 const std::size_t vi = lines[lid].inv;
1801 LnInv& v = invs[vi];
1802 const bool br = lines[lid].branch;
1803 if (v.pending < 0)
return complete_request(vi);
1804 const long rem = v.pending - 1;
1805 if (br) free_line(lid);
1807 invs[vi].pending = -1;
1808 return complete_request(vi);
1810 invs[vi].pending = rem;
1822 auto complete_activity = [&](std::size_t lid) -> std::size_t {
1823 finish_act(lines[lid]);
1824 const std::size_t act = lines[lid].act;
1825 const std::size_t vi = lines[lid].inv;
1826 if (access_of_driver[act] != LN_NONE) {
1827 const std::size_t nx = access_cache(access_of_driver[act], lid);
1828 if (nx == HELD)
return LN_NONE;
1832 const std::vector<std::size_t>& sc = succ_act[act];
1833 const bool br = lines[lid].branch;
1836 const std::size_t j = sc[0];
1837 std::set<std::size_t>& got = invs[vi].joins[j];
1839 if (br) free_line(lid);
1840 const std::size_t req = join_required[j] > 0 ? join_required[j] : 1;
1841 if (got.size() >= req) {
1842 invs[vi].joins.erase(j);
1843 if (invs[vi].pending >= 0) invs[vi].pending -=
static_cast<long>(req - 1);
1844 set_act(invs[vi].root, j);
1845 return invs[vi].root;
1849 if (!invs[vi].replied && !br) {
1850 bool reply = branch_reply[act] != 0 && branch_reply[act] == invs[vi].entry;
1851 if (!reply && phase_of(act) == 1)
1852 for (std::size_t s : sc)
1853 if (phase_of(s) == 2) {
1857 if (reply) send_reply(vi,
false);
1859 if (invs[vi].replied && invs[vi].fetching) release_fetch(vi);
1860 if (sc.empty())
return finish_line(lid);
1861 if (sc.size() == 1) {
1862 set_act(lid, sc[0]);
1865 if (!or_prob[act].empty()) {
1866 const double u = uniform01(g_route[act]);
1868 std::size_t pick = sc.back();
1869 for (std::size_t k = 0; k < sc.size(); ++k) {
1870 cum += or_prob[act][k];
1881 invs[vi].pending = (invs[vi].pending < 0 ? 1 : invs[vi].pending) +
1882 static_cast<long>(sc.size()) - 1;
1883 if (br) free_line(lid);
1884 std::vector<std::size_t> kids;
1885 for (std::size_t k = 0; k < sc.size(); ++k) kids.push_back(new_line(vi, sc[k],
true));
1886 for (std::size_t k = kids.size(); k-- > 1;) runnable.push_front(kids[k]);
1894 auto spawn_request = [&](std::size_t entry, std::size_t rep) {
1895 const std::size_t vi = new_inv(entry, rep, LN_NONE, LN_NONE);
1896 if (arrive_at_task(vi)) runnable.push_back(invs[vi].root);
1903 advance = [&](std::size_t lid) {
1904 while (lid != LN_NONE) {
1905 const std::size_t vi = lines[lid].inv;
1908 if (!invs[vi].has_thread) {
1909 if (!acquire_thread(vi))
return;
1913 if (!invs[vi].started) {
1914 LnInv& v = invs[vi];
1918 cur_q[v.entry] += 1.0;
1920 LnLine& L = lines[lid];
1921 const std::size_t act = L.act;
1923 lid = complete_request(vi);
1926 if (L.act_t0 < 0.0) {
1934 if (L.dem_done == 0) {
1936 if (has_hostdem[act]) {
1943 if (L.dem_done == 1) {
1945 if (has_actthink[act]) {
1947 e.t = now + actthink[act].next(g_actthink[act]);
1954 const std::vector<std::size_t>& calls =
1955 (act <
lsn.callsof.size()) ?
lsn.callsof[act] : std::vector<std::size_t>();
1956 if (L.call_pos < calls.size()) {
1959 const std::size_t cidx = calls[L.call_pos];
1960 if (L.calls_left == NOTDRAWN) L.calls_left = sample_call_count(cidx);
1961 if (L.calls_left == 0) {
1963 L.calls_left = NOTDRAWN;
1967 if (L.calls_left == 0) {
1969 L.calls_left = NOTDRAWN;
1971 const std::size_t callee =
lsn.callpair_dst[cidx];
1972 const std::size_t rep =
1973 callee_replica(invs[vi].task, invs[vi].trep,
lsn.parent[callee]);
1977 const std::size_t child = new_inv(callee, rep, lid, LN_NONE);
1978 if (!arrive_at_task(child))
return;
1979 lid = invs[child].root;
1984 spawn_request(callee, rep);
1990 L.calls_left = NOTDRAWN;
1991 lid = complete_activity(lid);
1998 auto start_cycle = [&](std::size_t c) {
1999 const std::size_t t = custs[c].ref_task;
2000 custs[c].t_start = now;
2001 const std::size_t vi = new_inv(
lsn.entriesof[t][0], custs[c].repl, LN_NONE, c);
2002 if (arrive_at_task(vi)) advance(invs[vi].root);
2004 bool any_ref =
false;
2005 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks; ++t) {
2006 if (t >=
lsn.isref.size() || !
lsn.isref[t])
continue;
2008 const std::size_t n = (task_threads[t] == std::numeric_limits<std::size_t>::max())
2011 const std::vector<std::size_t>& es =
2012 (t <
lsn.entriesof.size()) ?
lsn.entriesof[t] : std::vector<std::size_t>();
2014 throw InputError(
"SolverLDES (native LN engine): reference task '" +
lsn.names[t] +
2015 "' declares no entry to invoke");
2018 for (std::size_t m = 0; m < repl[t]; ++m) {
2019 for (std::size_t k = 0; k < n; ++k) {
2023 custs.push_back(cu);
2024 start_cycle(custs.size() - 1);
2031 std::vector<Sampler> arrival(nidx + 1);
2032 std::vector<Rng> g_arr;
2033 g_arr.reserve(nidx + 1);
2034 for (std::size_t k = 0; k <= nidx; ++k)
2035 g_arr.push_back(Rng(ln_seed,
static_cast<long long>(k) * 10 + 5000));
2036 for (std::size_t e =
lsn.eshift + 1; e <=
lsn.eshift +
lsn.nentries; ++e) {
2037 if (e >=
lsn.has_arrival.size() || !
lsn.has_arrival[e] || e >=
lsn.arrival.size() ||
2038 lsn.arrival[e].disabled)
2040 arrival[e] = Sampler(
lsn.arrival[e],
"the open arrivals at '" +
lsn.names[e] +
"'");
2041 for (std::size_t m = 0; m < repl[
lsn.parent[e]]; ++m) {
2043 ev.t = now + arrival[e].next(g_arr[e]);
2052 throw InputError(
"SolverLDES (native LN engine): the model has no reference task, so "
2053 "nothing drives it");
2056 while (!evq.empty() && done < max_events) {
2057 const LnEvent ev = evq.top();
2064 start_cycle(ev.who);
2069 if (ev.kind == 2 || ev.kind == 3) {
2070 const std::size_t ts = ev.who, th = ev.aux;
2071 if (ev.gen != thr_gen[ts][th])
continue;
2073 if (thr_state[ts][th] == ThreadState::DELAYOFF)
2074 thr_state[ts][th] = ThreadState::OFF;
2079 thr_state[ts][th] = ThreadState::ACTIVE;
2080 if (!thr_held[ts][th] && !thr_queue[ts].empty()) {
2081 const std::size_t nxt = dequeue(thr_queue[ts], task_sched[thr_owner[ts]], inv_key);
2082 thr_held[ts][th] =
true;
2083 touch(thr_owner[ts]);
2084 cur_u[thr_owner[ts]] += 1.0;
2085 invs[nxt].has_thread =
true;
2087 advance(invs[nxt].root);
2095 const std::size_t hs = ev.who;
2096 if (ev.gen != pool_gen[hs])
continue;
2097 const std::size_t host = host_of_slot[hs];
2099 std::vector<std::size_t> finished, keep;
2100 for (std::size_t lid : pool_jobs[hs])
2101 (lines[lid].remaining <= 1e-9 ? finished : keep).push_back(lid);
2102 pool_jobs[hs].swap(keep);
2104 for (std::size_t lid : finished) {
2107 completions[host] += 1.0;
2108 act_host_resid[lines[lid].act] += now - lines[lid].host_t0;
2109 release_host_admission(lid);
2118 const std::size_t hs = ev.who;
2119 if (ev.gen != ps_gen[hs])
continue;
2120 const std::size_t host = host_of_slot[hs];
2122 std::vector<std::size_t> finished, keep;
2123 for (std::size_t lid : ps_jobs[hs])
2124 (lines[lid].remaining <= 1e-9 ? finished : keep).push_back(lid);
2125 const std::size_t n0 = ps_jobs[hs].size();
2126 ps_jobs[hs].swap(keep);
2128 cur_u[host] -= ps_busy(hs, n0) - ps_busy(hs, ps_jobs[hs].size());
2130 for (std::size_t lid : finished) {
2133 completions[host] += 1.0;
2134 act_host_resid[lines[lid].act] += now - lines[lid].host_t0;
2135 release_host_admission(lid);
2151 spawn_request(ev.who, ev.aux);
2153 nx.t = now + arrival[ev.who].next(g_arr[ev.who]);
2160 const std::size_t lid = ev.who;
2161 if (ev.gen != lines[lid].host_gen)
continue;
2162 const std::size_t act = lines[lid].act;
2163 const std::size_t host =
lsn.host_of(act);
2166 const std::size_t hs = host_slot(host, invs[lines[lid].inv].trep);
2171 completions[host] += 1.0;
2172 act_host_resid[act] += now - lines[lid].host_t0;
2173 if (preemptive(host_sched[host]))
2174 in_service[hs].erase(std::find(in_service[hs].begin(), in_service[hs].end(), lid));
2175 if (!host_queue[hs].empty()) {
2176 const std::size_t nxt = dequeue(host_queue[hs], host_sched[host], line_key);
2179 begin_host(nxt, hs);
2181 release_host_admission(lid);
2191 if (done < max_events && evq.empty()) {
2192 for (std::size_t s = 0; s < nhost_slots; ++s)
2193 if (!hadm[s].empty())
2194 throw InputError(
"SolverLDES (native LN engine): the admission constraint on "
2195 "host '" +
lsn.names[host_of_slot[s]] +
"' deadlocked the model: " +
2196 std::to_string(hadm[s].size()) +
2197 " request(s) are blocked with no event left");
2198 for (std::size_t s = 0; s < ntask_slots; ++s)
2199 if (!tadm[s].empty())
2200 throw InputError(
"SolverLDES (native LN engine): the admission constraint on "
2201 "task '" +
lsn.names[thr_owner[s]] +
"' deadlocked the model: " +
2202 std::to_string(tadm[s].size()) +
2203 " request(s) are blocked with no event left");
2207 for (std::size_t k = 1; k <= nidx; ++k) touch(k);
2216 res.WLN =
Matrix<double>(nidx + 1, 1, std::numeric_limits<double>::quiet_NaN());
2217 for (std::size_t k = 1; k <= nidx; ++k) {
2219 res.QLN(k, 0) = tot_q[k] / now;
2227 const bool is_task = (k >
lsn.tshift && k <=
lsn.tshift +
lsn.ntasks);
2228 const std::size_t cap_k = is_task ? task_threads[k] : host_servers[k];
2229 const double c = (cap_k == std::numeric_limits<std::size_t>::max())
2231 :
static_cast<double>(cap_k * repl[k]);
2232 res.ULN(k, 0) = tot_u[k] / (now * c);
2233 res.TLN(k, 0) = completions[k] / now;
2235 if (resp_cnt[k] > 0.0) res.RLN(k, 0) = resp_sum[k] / resp_cnt[k];
2237 res.entry_resp_samples.swap(entry_resp_samples);
2253 std::vector<double> task_resid(nidx + 1, 0.0);
2254 std::vector<bool> task_has_act(nidx + 1,
false);
2255 for (std::size_t a =
lsn.ashift + 1; a <=
lsn.ashift +
lsn.nacts && a <= nidx; ++a) {
2256 const std::size_t task =
lsn.parent[a];
2257 const double x_task = (task >= 1 && task <= nidx) ? res.TLN(task, 0) : 0.0;
2258 const double resid = (x_task > 0.0) ? act_host_resid[a] / (now * x_task) : 0.0;
2259 res.WLN(a, 0) = resid;
2260 if (task >= 1 && task <= nidx) {
2261 task_resid[task] += resid;
2262 task_has_act[task] =
true;
2265 for (std::size_t t =
lsn.tshift + 1; t <=
lsn.tshift +
lsn.ntasks && t <= nidx; ++t)
2266 if (task_has_act[t]) res.WLN(t, 0) = task_resid[t];
2283 std::vector<double> proc_util(nidx + 1, 0.0), task_proc_util(nidx + 1, 0.0);
2284 for (std::size_t a =
lsn.ashift + 1; a <=
lsn.ashift +
lsn.nacts && a <= nidx; ++a) {
2285 const double d = (a <
lsn.hostdem.size() && !
lsn.hostdem[a].disabled)
2288 proc_util[a] = res.TLN(a, 0) * d;
2289 const std::size_t task =
lsn.parent[a];
2290 if (task >= 1 && task <= nidx) task_proc_util[task] += proc_util[a];
2292 for (std::size_t e =
lsn.eshift + 1; e <=
lsn.eshift +
lsn.nentries && e <= nidx; ++e) {
2294 const std::vector<std::size_t>& acts =
2295 (e <
lsn.actsof.size()) ?
lsn.actsof[e] : std::vector<std::size_t>();
2296 for (std::size_t i = 0; i < acts.size(); ++i)
2297 if (acts[i] <= nidx) u += proc_util[acts[i]];
2300 for (std::size_t a =
lsn.ashift + 1; a <=
lsn.ashift +
lsn.nacts && a <= nidx; ++a) {
2301 const std::size_t task =
lsn.parent[a];
2302 const double tu = (task >= 1 && task <= nidx) ? task_proc_util[task] : 0.0;
2303 if (tu > 0.0 && tu < 1.0)
2304 res.ULN(a, 0) = std::min(1.0, proc_util[a] / tu);
2306 res.ULN(a, 0) = (proc_util[a] > 0.0) ? 1.0 : 0.0;
2311 for (std::size_t ai = 0; ai < accesses.size(); ++ai) {
2312 long long reads = 0, hits = 0, misses = 0, delayed = 0;
2313 for (std::size_t ci : accesses[ai].caches) {
2314 reads += caches[ci].reads;
2315 hits += caches[ci].hits;
2316 misses += caches[ci].misses;
2317 delayed += caches[ci].delayed;
2319 if (reads <= 0 || !(now > 0.0))
continue;
2320 const double n =
static_cast<double>(reads);
2321 res.cache_task.push_back(
lsn.parent[accesses[ai].entry]);
2322 res.cache_entry.push_back(accesses[ai].entry);
2323 res.cache_hit.push_back(
static_cast<double>(hits) / n);
2324 res.cache_miss.push_back(
static_cast<double>(misses) / n);
2325 res.cache_delayed.push_back(
static_cast<double>(delayed) / n);
2326 res.cache_read_rate.push_back(n / now);
2329 res.simulated_time = now;
2330 res.completions =
static_cast<long long>(done);