| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "nalitov_d_dijkstras_algorithm/tbb/include/ops_tbb.hpp" | ||
| 2 | |||
| 3 | #include <oneapi/tbb/blocked_range.h> | ||
| 4 | #include <oneapi/tbb/parallel_for.h> | ||
| 5 | #include <oneapi/tbb/parallel_reduce.h> | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <atomic> | ||
| 9 | #include <cstddef> | ||
| 10 | #include <cstdint> | ||
| 11 | #include <functional> | ||
| 12 | #include <utility> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | #include "nalitov_d_dijkstras_algorithm/common/include/common.hpp" | ||
| 16 | |||
| 17 | namespace nalitov_d_dijkstras_algorithm { | ||
| 18 | |||
| 19 | namespace { | ||
| 20 | |||
| 21 | struct BestVertex { | ||
| 22 | Cost cost{kInf}; | ||
| 23 | NodeId vtx{-1}; | ||
| 24 | |||
| 25 | [[nodiscard]] bool IsBetterThan(const BestVertex &other) const { | ||
| 26 |
3/20✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 100 times.
✓ Branch 11 taken 60 times.
✓ Branch 12 taken 100 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
160 | return cost < other.cost || (cost == other.cost && vtx != -1 && (other.vtx == -1 || vtx < other.vtx)); |
| 27 | } | ||
| 28 | |||
| 29 | void Merge(const BestVertex &other) { | ||
| 30 | if (other.IsBetterThan(*this)) { | ||
| 31 | cost = other.cost; | ||
| 32 | ✗ | vtx = other.vtx; | |
| 33 | } | ||
| 34 | } | ||
| 35 | }; | ||
| 36 | |||
| 37 | 260 | BestVertex FindLocalBestVertex(const std::vector<char> &visited, const std::vector<std::atomic<Cost>> &atomic_dist, | |
| 38 | const tbb::blocked_range<int> &range, BestVertex acc) { | ||
| 39 |
2/2✓ Branch 0 taken 260 times.
✓ Branch 1 taken 260 times.
|
520 | for (int vi = range.begin(); vi < range.end(); ++vi) { |
| 40 |
2/2✓ Branch 0 taken 100 times.
✓ Branch 1 taken 160 times.
|
260 | const auto idx = static_cast<std::size_t>(vi); |
| 41 | |||
| 42 |
2/2✓ Branch 0 taken 100 times.
✓ Branch 1 taken 160 times.
|
260 | if (visited[idx] != 0) { |
| 43 | continue; | ||
| 44 | } | ||
| 45 | |||
| 46 | const Cost dist = atomic_dist[idx].load(std::memory_order_relaxed); | ||
| 47 | |||
| 48 | const BestVertex candidate{ | ||
| 49 | .cost = dist, | ||
| 50 | .vtx = vi, | ||
| 51 | }; | ||
| 52 | |||
| 53 | if (candidate.IsBetterThan(acc)) { | ||
| 54 | acc = candidate; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | 260 | return acc; | |
| 59 | } | ||
| 60 | |||
| 61 | 60 | BestVertex SelectBestVertex(const std::vector<char> &visited, const std::vector<std::atomic<Cost>> &atomic_dist, | |
| 62 | int n) { | ||
| 63 | 60 | return tbb::parallel_reduce(tbb::blocked_range<int>(0, n), BestVertex{}, [&](const auto &range, BestVertex acc) { | |
| 64 | 260 | return FindLocalBestVertex(visited, atomic_dist, range, acc); | |
| 65 | 60 | }, [](BestVertex lhs, const BestVertex &rhs) { | |
| 66 | lhs.Merge(rhs); | ||
| 67 | return lhs; | ||
| 68 | 60 | }); | |
| 69 | } | ||
| 70 | |||
| 71 | void RelaxEdge(std::atomic<Cost> &target, Cost candidate) { | ||
| 72 | Cost old = target.load(std::memory_order_relaxed); | ||
| 73 | |||
| 74 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
48 | while (candidate < old) { |
| 75 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if (target.compare_exchange_weak(old, candidate, std::memory_order_relaxed)) { |
| 76 | break; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | 60 | void RelaxNeighbors(const std::vector<std::pair<NodeId, Cost>> &neighbors, Cost pivot_cost, | |
| 82 | const std::vector<char> &visited, std::vector<std::atomic<Cost>> &atomic_dist) { | ||
| 83 | 108 | tbb::parallel_for(tbb::blocked_range<std::size_t>(0, neighbors.size()), [&](const auto &range) { | |
| 84 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 48 times.
|
96 | for (std::size_t ei = range.begin(); ei < range.end(); ++ei) { |
| 85 |
1/2✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
|
48 | const auto [target, weight] = neighbors[ei]; |
| 86 | 48 | const auto target_idx = static_cast<std::size_t>(target); | |
| 87 | |||
| 88 |
2/4✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
|
48 | if (visited[target_idx] != 0 || pivot_cost > kInf - weight) { |
| 89 | ✗ | continue; | |
| 90 | } | ||
| 91 | |||
| 92 | 48 | RelaxEdge(atomic_dist[target_idx], pivot_cost + weight); | |
| 93 | } | ||
| 94 | 48 | }); | |
| 95 | 60 | } | |
| 96 | |||
| 97 | 16 | std::int64_t SumDistances(const std::vector<std::atomic<Cost>> &atomic_dist, std::size_t n) { | |
| 98 | 32 | return tbb::parallel_reduce(tbb::blocked_range<std::size_t>(0, n), std::int64_t{0}, | |
| 99 | 76 | [&](const auto &range, std::int64_t acc) { | |
| 100 |
2/2✓ Branch 0 taken 60 times.
✓ Branch 1 taken 60 times.
|
120 | for (std::size_t i = range.begin(); i < range.end(); ++i) { |
| 101 |
1/2✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
|
60 | const Cost dist = atomic_dist[i].load(std::memory_order_relaxed); |
| 102 | |||
| 103 |
1/2✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
|
60 | if (dist != kInf) { |
| 104 | 60 | acc += static_cast<std::int64_t>(dist); | |
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | 60 | return acc; | |
| 109 | 16 | }, std::plus<>()); | |
| 110 | } | ||
| 111 | |||
| 112 | } // namespace | ||
| 113 | |||
| 114 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | NalitovDDijkstrasAlgorithmTBB::NalitovDDijkstrasAlgorithmTBB(const InType &in) { |
| 115 | SetTypeOfTask(GetStaticTypeOfTask()); | ||
| 116 | GetInput() = in; | ||
| 117 | 16 | GetOutput() = 0; | |
| 118 | 16 | } | |
| 119 | |||
| 120 | 16 | bool NalitovDDijkstrasAlgorithmTBB::ValidationImpl() { | |
| 121 | const InType &in = GetInput(); | ||
| 122 | |||
| 123 |
3/6✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
|
16 | if (in.n <= 0 || in.n > 10000 || in.source < 0 || in.source >= in.n) { |
| 124 | return false; | ||
| 125 | } | ||
| 126 | |||
| 127 | return std::ranges::all_of(in.arcs, [&in](const Arc &arc) { | ||
| 128 |
5/10✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 48 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 48 times.
✗ Branch 9 not taken.
|
48 | return arc.from >= 0 && arc.to >= 0 && arc.from < in.n && arc.to < in.n && arc.weight >= 0; |
| 129 | }); | ||
| 130 | } | ||
| 131 | |||
| 132 | 16 | bool NalitovDDijkstrasAlgorithmTBB::PreProcessingImpl() { | |
| 133 | const InType &in = GetInput(); | ||
| 134 | 16 | const auto vertex_count = static_cast<std::size_t>(in.n); | |
| 135 | |||
| 136 | 16 | std::vector<std::size_t> out_degree(vertex_count, 0); | |
| 137 | |||
| 138 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 16 times.
|
64 | for (const Arc &arc : in.arcs) { |
| 139 | 48 | ++out_degree[static_cast<std::size_t>(arc.from)]; | |
| 140 | } | ||
| 141 | |||
| 142 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | graph_.assign(vertex_count, {}); |
| 143 | |||
| 144 |
1/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
16 | std::vector<std::atomic<std::size_t>> row_next(vertex_count); |
| 145 | |||
| 146 |
2/2✓ Branch 0 taken 60 times.
✓ Branch 1 taken 16 times.
|
76 | for (std::size_t vi = 0; vi < vertex_count; ++vi) { |
| 147 |
1/2✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
|
60 | graph_[vi].resize(out_degree[vi]); |
| 148 | row_next[vi].store(0, std::memory_order_relaxed); | ||
| 149 | } | ||
| 150 | |||
| 151 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
64 | tbb::parallel_for(tbb::blocked_range<std::size_t>(0, in.arcs.size()), [&](const auto &range) { |
| 152 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 48 times.
|
96 | for (std::size_t ei = range.begin(); ei < range.end(); ++ei) { |
| 153 | 48 | const Arc &arc = in.arcs[ei]; | |
| 154 | 48 | const auto from = static_cast<std::size_t>(arc.from); | |
| 155 | |||
| 156 | 48 | const std::size_t slot = row_next[from].fetch_add(1, std::memory_order_relaxed); | |
| 157 | 48 | graph_[from][slot] = {arc.to, arc.weight}; | |
| 158 | } | ||
| 159 | 48 | }); | |
| 160 | |||
| 161 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | dist_.assign(vertex_count, kInf); |
| 162 |
1/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
16 | visited_.assign(vertex_count, 0); |
| 163 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | dist_[static_cast<std::size_t>(in.source)] = 0; |
| 164 | |||
| 165 | 16 | return true; | |
| 166 | } | ||
| 167 | |||
| 168 | 16 | bool NalitovDDijkstrasAlgorithmTBB::RunImpl() { | |
| 169 | 16 | const int n = GetInput().n; | |
| 170 | 16 | const auto vertex_count = static_cast<std::size_t>(n); | |
| 171 | |||
| 172 | 16 | std::vector<std::atomic<Cost>> atomic_dist(vertex_count); | |
| 173 | |||
| 174 |
2/2✓ Branch 0 taken 60 times.
✓ Branch 1 taken 16 times.
|
76 | for (std::size_t vi = 0; vi < vertex_count; ++vi) { |
| 175 | 60 | atomic_dist[vi].store(dist_[vi], std::memory_order_relaxed); | |
| 176 | } | ||
| 177 | |||
| 178 |
2/2✓ Branch 0 taken 60 times.
✓ Branch 1 taken 16 times.
|
76 | for (int step = 0; step < n; ++step) { |
| 179 |
1/2✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
|
60 | const BestVertex pivot = SelectBestVertex(visited_, atomic_dist, n); |
| 180 | |||
| 181 |
2/4✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 60 times.
✗ Branch 3 not taken.
|
60 | if (pivot.vtx == -1 || pivot.cost == kInf) { |
| 182 | break; | ||
| 183 | } | ||
| 184 | |||
| 185 |
1/2✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
|
60 | visited_[static_cast<std::size_t>(pivot.vtx)] = 1; |
| 186 | |||
| 187 | const auto &neighbors = graph_[static_cast<std::size_t>(pivot.vtx)]; | ||
| 188 |
1/2✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
|
60 | RelaxNeighbors(neighbors, pivot.cost, visited_, atomic_dist); |
| 189 | } | ||
| 190 | |||
| 191 |
2/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
|
16 | GetOutput() = static_cast<OutType>(SumDistances(atomic_dist, vertex_count)); |
| 192 | |||
| 193 | 16 | return true; | |
| 194 | } | ||
| 195 | |||
| 196 | 16 | bool NalitovDDijkstrasAlgorithmTBB::PostProcessingImpl() { | |
| 197 | 16 | return GetOutput() >= 0; | |
| 198 | } | ||
| 199 | |||
| 200 | } // namespace nalitov_d_dijkstras_algorithm | ||
| 201 |