From 61d2642be381a5691a3a2ba72058f9b145d609b8 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Sat, 12 Aug 2023 17:43:54 +0200 Subject: [PATCH 01/10] First take at systematic use of reserve. --- src/algorithms/heuristics/heuristics.cpp | 22 ++++++++++++++++--- .../local_search/insertion_search.h | 7 +++++- src/algorithms/local_search/local_search.cpp | 13 ++++++++++- src/problems/cvrp/operators/pd_shift.cpp | 5 ++++- src/problems/vrp.h | 4 ++++ src/problems/vrptw/operators/pd_shift.cpp | 5 ++++- src/routing/http_wrapper.cpp | 2 ++ src/routing/libosrm_wrapper.cpp | 3 +++ src/structures/vroom/input/input.cpp | 2 +- src/structures/vroom/vehicle.cpp | 2 ++ src/utils/helpers.h | 5 +++++ src/utils/input_parser.cpp | 2 ++ 12 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/algorithms/heuristics/heuristics.cpp b/src/algorithms/heuristics/heuristics.cpp index 54b1183d6..db2fadeb4 100644 --- a/src/algorithms/heuristics/heuristics.cpp +++ b/src/algorithms/heuristics/heuristics.cpp @@ -64,6 +64,8 @@ template T basic(const Input& input, INIT init, double lambda, SORT sort) { const auto nb_vehicles = input.vehicles.size(); T routes; + routes.reserve(nb_vehicles); + for (Index v = 0; v < nb_vehicles; ++v) { routes.emplace_back(input, v, input.zero_amount().size()); } @@ -346,7 +348,10 @@ T basic(const Input& input, INIT init, double lambda, SORT sort) { } // Build replacement sequence for current insertion. - std::vector modified_with_pd({job_rank}); + std::vector modified_with_pd; + modified_with_pd.reserve(current_r.size() - pickup_r + 3); + modified_with_pd.push_back(job_rank); + Amount modified_delivery = input.zero_amount(); for (Index delivery_r = pickup_r; delivery_r <= current_r.size(); @@ -431,7 +436,10 @@ T basic(const Input& input, INIT init, double lambda, SORT sort) { keep_going = true; } if (input.jobs[best_job_rank].type == JOB_TYPE::PICKUP) { - std::vector modified_with_pd({best_job_rank}); + std::vector modified_with_pd; + modified_with_pd.reserve(best_delivery_r - best_pickup_r + 2); + modified_with_pd.push_back(best_job_rank); + std::copy(current_r.route.begin() + best_pickup_r, current_r.route.begin() + best_delivery_r, std::back_inserter(modified_with_pd)); @@ -463,6 +471,8 @@ T dynamic_vehicle_choice(const Input& input, SORT sort) { const auto nb_vehicles = input.vehicles.size(); T routes; + routes.reserve(nb_vehicles); + for (Index v = 0; v < nb_vehicles; ++v) { routes.emplace_back(input, v, input.zero_amount().size()); } @@ -777,7 +787,10 @@ T dynamic_vehicle_choice(const Input& input, } // Build replacement sequence for current insertion. - std::vector modified_with_pd({job_rank}); + std::vector modified_with_pd; + modified_with_pd.reserve(current_r.size() - pickup_r + 3); + modified_with_pd.push_back(job_rank); + Amount modified_delivery = input.zero_amount(); for (Index delivery_r = pickup_r; delivery_r <= current_r.size(); @@ -860,6 +873,9 @@ T dynamic_vehicle_choice(const Input& input, } if (input.jobs[best_job_rank].type == JOB_TYPE::PICKUP) { std::vector modified_with_pd({best_job_rank}); + modified_with_pd.reserve(best_delivery_r - best_pickup_r + 2); + modified_with_pd.push_back(best_job_rank); + std::copy(current_r.route.begin() + best_pickup_r, current_r.route.begin() + best_delivery_r, std::back_inserter(modified_with_pd)); diff --git a/src/algorithms/local_search/insertion_search.h b/src/algorithms/local_search/insertion_search.h index fd19a93a8..c0ce7ed6a 100644 --- a/src/algorithms/local_search/insertion_search.h +++ b/src/algorithms/local_search/insertion_search.h @@ -137,7 +137,12 @@ RouteInsertion compute_best_insertion_pd(const Input& input, } // Build replacement sequence for current insertion. - std::vector modified_with_pd({j}); + std::vector modified_with_pd; + if (pickup_r < end_d_rank) { + modified_with_pd.reserve(end_d_rank - pickup_r + 2); + } + modified_with_pd.push_back(j); + Amount modified_delivery = input.zero_amount(); // No need to use begin_d_rank here thanks to diff --git a/src/algorithms/local_search/local_search.cpp b/src/algorithms/local_search/local_search.cpp index ce19bdfe2..9062a7134 100644 --- a/src/algorithms/local_search/local_search.cpp +++ b/src/algorithms/local_search/local_search.cpp @@ -155,6 +155,7 @@ void LocalSearch> route_job_insertions; + route_job_insertions.reserve(routes.size()); for (std::size_t i = 0; i < routes.size(); ++i) { route_job_insertions.emplace_back(_input.jobs.size(), @@ -258,7 +259,11 @@ void LocalSearch modified_with_pd({best_job_rank}); + std::vector modified_with_pd; + modified_with_pd.reserve(best_insertion.delivery_rank - + best_insertion.pickup_rank + 2); + modified_with_pd.push_back(best_job_rank); + std::copy(_sol[best_route].route.begin() + best_insertion.pickup_rank, _sol[best_route].route.begin() + best_insertion.delivery_rank, std::back_inserter(modified_with_pd)); @@ -348,6 +353,8 @@ void LocalSearch> s_t_pairs; + s_t_pairs.reserve(_nb_vehicles * _nb_vehicles); + for (unsigned s_v = 0; s_v < _nb_vehicles; ++s_v) { for (unsigned t_v = 0; t_v < _nb_vehicles; ++t_v) { if (_input.vehicle_ok_with_vehicle(s_v, t_v)) { @@ -1630,7 +1637,10 @@ void LocalSearch empty_route_ranks; + empty_route_ranks.reserve(_input.vehicles.size()); std::vector> empty_route_refs; + empty_route_refs.reserve(_input.vehicles.size()); + for (Index v = 0; v < _input.vehicles.size(); ++v) { if (_sol[v].empty()) { empty_route_ranks.push_back(v); @@ -2191,6 +2201,7 @@ void LocalSearch> routes_and_ranks; + routes_and_ranks.reserve(_sol.size()); for (std::size_t v = 0; v < _sol.size(); ++v) { if (_sol[v].empty()) { diff --git a/src/problems/cvrp/operators/pd_shift.cpp b/src/problems/cvrp/operators/pd_shift.cpp index 630293bd1..047e7343c 100644 --- a/src/problems/cvrp/operators/pd_shift.cpp +++ b/src/problems/cvrp/operators/pd_shift.cpp @@ -81,7 +81,10 @@ bool PDShift::is_valid() { } void PDShift::apply() { - std::vector target_with_pd({s_route[_s_p_rank]}); + std::vector target_with_pd; + target_with_pd.reserve(_best_t_d_rank - _best_t_p_rank + 2); + target_with_pd.push_back(s_route[_s_p_rank]); + std::copy(t_route.begin() + _best_t_p_rank, t_route.begin() + _best_t_d_rank, std::back_inserter(target_with_pd)); diff --git a/src/problems/vrp.h b/src/problems/vrp.h index 97aab22ad..0c1e6ed61 100644 --- a/src/problems/vrp.h +++ b/src/problems/vrp.h @@ -135,6 +135,7 @@ class VRP { }; std::vector heuristics_threads; + heuristics_threads.reserve(nb_threads); for (const auto& param_ranks : thread_ranks) { if (!param_ranks.empty()) { @@ -153,6 +154,8 @@ class VRP { // Filter out duplicate heuristics solutions. std::set> unique_indicators; std::vector to_remove; + to_remove.reserve(solutions.size()); + for (unsigned i = 0; i < solutions.size(); ++i) { const auto result = unique_indicators.emplace(_input, solutions[i]); if (!result.second) { @@ -211,6 +214,7 @@ class VRP { }; std::vector ls_threads; + ls_threads.reserve(nb_threads); for (const auto& sol_ranks : thread_ranks) { if (!sol_ranks.empty()) { diff --git a/src/problems/vrptw/operators/pd_shift.cpp b/src/problems/vrptw/operators/pd_shift.cpp index 93ff8216d..55b1b348a 100644 --- a/src/problems/vrptw/operators/pd_shift.cpp +++ b/src/problems/vrptw/operators/pd_shift.cpp @@ -69,7 +69,10 @@ void PDShift::compute_gain() { } void PDShift::apply() { - std::vector target_with_pd({s_route[_s_p_rank]}); + std::vector target_with_pd; + target_with_pd.reserve(_best_t_d_rank - _best_t_p_rank + 2); + target_with_pd.push_back(s_route[_s_p_rank]); + std::copy(t_route.begin() + _best_t_p_rank, t_route.begin() + _best_t_d_rank, std::back_inserter(target_with_pd)); diff --git a/src/routing/http_wrapper.cpp b/src/routing/http_wrapper.cpp index 645bb4444..be30012fe 100644 --- a/src/routing/http_wrapper.cpp +++ b/src/routing/http_wrapper.cpp @@ -203,7 +203,9 @@ void HttpWrapper::add_route_info(Route& route) const { // Ordering locations for the given steps, excluding // breaks. std::vector non_break_locations; + non_break_locations.reserve(route.steps.size()); std::vector number_breaks_after; + number_breaks_after.reserve(route.steps.size()); for (const auto& step : route.steps) { if (step.step_type == STEP_TYPE::BREAK) { diff --git a/src/routing/libosrm_wrapper.cpp b/src/routing/libosrm_wrapper.cpp index d87f63754..1e6b4ca83 100644 --- a/src/routing/libosrm_wrapper.cpp +++ b/src/routing/libosrm_wrapper.cpp @@ -126,10 +126,13 @@ void LibosrmWrapper::add_route_info(Route& route) const { osrm::RouteParameters::OverviewType::Full, false // continue_straight ); + params.coordinates.reserve(route.steps.size()); // Ordering locations for the given steps, excluding // breaks. std::vector number_breaks_after; + number_breaks_after.reserve(route.steps.size()); + for (auto& step : route.steps) { if (step.step_type == STEP_TYPE::BREAK) { if (!number_breaks_after.empty()) { diff --git a/src/structures/vroom/input/input.cpp b/src/structures/vroom/input/input.cpp index bcfda1b25..14676de54 100644 --- a/src/structures/vroom/input/input.cpp +++ b/src/structures/vroom/input/input.cpp @@ -958,8 +958,8 @@ void Input::set_matrices(unsigned nb_thread) { }; std::vector matrix_threads; - matrix_threads.reserve(thread_profiles.size()); + for (const auto& profiles : thread_profiles) { matrix_threads.emplace_back(run_on_profiles, profiles); } diff --git a/src/structures/vroom/vehicle.cpp b/src/structures/vroom/vehicle.cpp index 26f1a3d67..0632cd67a 100644 --- a/src/structures/vroom/vehicle.cpp +++ b/src/structures/vroom/vehicle.cpp @@ -71,6 +71,8 @@ Vehicle::Vehicle(Id id, if (!input_steps.empty()) { // Populating steps. We rely on always having start and end steps // in input, so just add them if they're missing. + steps.reserve(input_steps.size() + 2); + unsigned rank_after_start = 0; if (input_steps.front().type == STEP_TYPE::START) { steps.push_back(input_steps.front()); diff --git a/src/utils/helpers.h b/src/utils/helpers.h index 678ccae08..bc3283490 100644 --- a/src/utils/helpers.h +++ b/src/utils/helpers.h @@ -136,6 +136,7 @@ inline HeuristicParameters str_to_heuristic_param(const std::string& s) { // Split command-line string describing parameters. constexpr char delimiter = ';'; std::vector tokens; + tokens.reserve(4); std::string token; std::istringstream tokenStream(s); while (std::getline(tokenStream, token, delimiter)) { @@ -411,6 +412,7 @@ inline void check_priority(const Priority priority, inline Solution format_solution(const Input& input, const RawSolution& raw_routes) { std::vector routes; + routes.reserve(raw_routes.size()); // All job ranks start with unassigned status. std::unordered_set unassigned_ranks; @@ -444,6 +446,7 @@ inline Solution format_solution(const Input& input, // Steps for current route. std::vector steps; + steps.reserve(route.size() + 2); Duration ETA = 0; const auto& first_job = input.jobs[route.front()]; @@ -668,6 +671,7 @@ inline Route format_route(const Input& input, // Steps for current route. std::vector steps; + steps.reserve(tw_r.size() + 2 + v.breaks.size()); // Now pack everything ASAP based on first job start date. Duration remaining_travel_time = @@ -1025,6 +1029,7 @@ inline Route format_route(const Input& input, inline Solution format_solution(const Input& input, const TWSolution& tw_routes) { std::vector routes; + routes.reserve(tw_routes.size()); // All job ranks start with unassigned status. std::unordered_set unassigned_ranks; diff --git a/src/utils/input_parser.cpp b/src/utils/input_parser.cpp index 8d2b752bc..903ef30bf 100644 --- a/src/utils/input_parser.cpp +++ b/src/utils/input_parser.cpp @@ -281,6 +281,8 @@ inline std::vector get_vehicle_steps(const rapidjson::Value& v) { std::to_string(v["id"].GetUint64()) + "."); } + steps.reserve(v["steps"].Size()); + for (rapidjson::SizeType i = 0; i < v["steps"].Size(); ++i) { const auto& json_step = v["steps"][i]; From d576a1398cd01e5ea39cae9da5860c2b07e429f8 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Sun, 13 Aug 2023 09:47:11 +0200 Subject: [PATCH 02/10] Reserve in swap_start_utils. --- src/algorithms/local_search/swap_star_utils.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/algorithms/local_search/swap_star_utils.h b/src/algorithms/local_search/swap_star_utils.h index 379c6c8ad..28476369a 100644 --- a/src/algorithms/local_search/swap_star_utils.h +++ b/src/algorithms/local_search/swap_star_utils.h @@ -189,6 +189,7 @@ inline InsertionRange get_insert_range(const std::vector& s_route, insert.last_rank = s_rank + 1; } else { if (s_rank < insertion_rank) { + insert.range.reserve(insertion_rank - s_rank); std::copy(s_route.begin() + s_rank + 1, s_route.begin() + insertion_rank, std::back_inserter(insert.range)); @@ -196,6 +197,7 @@ inline InsertionRange get_insert_range(const std::vector& s_route, insert.first_rank = s_rank; insert.last_rank = insertion_rank; } else { + insert.range.reserve(s_rank - insertion_rank + 1); insert.range.push_back(job_rank); std::copy(s_route.begin() + insertion_rank, s_route.begin() + s_rank, @@ -295,6 +297,7 @@ SwapChoice compute_best_swap_star_choice(const Input& input, s_rank); std::vector swap_choice_options; + swap_choice_options.reserve(16); // Options for in-place insertion in source route include // in-place insertion in target route and other relevant From 90b9bac94b1ca5a7e0587f282bad1cadbc39fed1 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Sun, 13 Aug 2023 10:24:49 +0200 Subject: [PATCH 03/10] Reserve in plan mode code. --- src/algorithms/validation/check.cpp | 4 +++- src/algorithms/validation/choose_ETA.cpp | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/algorithms/validation/check.cpp b/src/algorithms/validation/check.cpp index 27c0df928..0a9f523c1 100644 --- a/src/algorithms/validation/check.cpp +++ b/src/algorithms/validation/check.cpp @@ -77,8 +77,8 @@ Solution check_and_set_ETA(const Input& input, unsigned nb_thread) { }; std::vector solving_threads; - solving_threads.reserve(thread_ranks.size()); + for (const auto& v_ranks : thread_ranks) { solving_threads.emplace_back(run_check, v_ranks); } @@ -93,6 +93,8 @@ Solution check_and_set_ETA(const Input& input, unsigned nb_thread) { // Handle unassigned jobs. std::vector unassigned_jobs; + unassigned_jobs.reserve(input.jobs.size() - assigned_ranks.size()); + for (Index j = 0; j < input.jobs.size(); ++j) { if (assigned_ranks.find(j) == assigned_ranks.end()) { unassigned_jobs.push_back(input.jobs[j]); diff --git a/src/algorithms/validation/choose_ETA.cpp b/src/algorithms/validation/choose_ETA.cpp index 94be9d472..d5c4d5ce5 100644 --- a/src/algorithms/validation/choose_ETA.cpp +++ b/src/algorithms/validation/choose_ETA.cpp @@ -70,6 +70,11 @@ Route choose_ETA(const Input& input, std::vector B; std::vector durations; std::vector action_times; + J.reserve(n + 1); + B.reserve(n + 1); + durations.reserve(n + 1); + action_times.reserve(n + 1); + // Lower bound for timestamps in input in order to scale the MIP // matrix values. Duration horizon_start = std::numeric_limits::max(); @@ -87,6 +92,8 @@ Route choose_ETA(const Input& input, unsigned default_job_tw = 0; Duration relative_arrival = 0; std::vector relative_ETA; + relative_ETA.reserve(steps.size()); + std::optional previous_index; std::optional first_location; std::optional last_location; @@ -395,6 +402,10 @@ Route choose_ETA(const Input& input, std::vector first_relevant_tw_rank; Index rank_in_J = 0; + t_i_LB.reserve(steps.size()); + t_i_UB.reserve(steps.size()); + first_relevant_tw_rank.reserve(n); + for (const auto& step : steps) { // Derive basic bounds from user input. Duration LB = horizon_start; @@ -1043,6 +1054,9 @@ Route choose_ETA(const Input& input, std::vector task_ETA; std::vector task_travels; + task_ETA.reserve(n); + task_travels.reserve(n); + for (unsigned i = 0; i < n; ++i) { task_ETA.push_back(horizon_start + get_duration(glp_mip_col_val(lp, i + 2))); @@ -1053,6 +1067,7 @@ Route choose_ETA(const Input& input, // Populate vector storing picked time window ranks. current_X_rank = start_X_col; std::vector task_tw_ranks; + task_tw_ranks.reserve(n); for (const auto& step : steps) { switch (step.type) { @@ -1130,6 +1145,7 @@ Route choose_ETA(const Input& input, [](const auto& b) { return b.id; }); std::vector sol_steps; + sol_steps.reserve(steps.size()); assert(v.has_start() or start_travel == 0); From 1fb66916d39a10105cb5e5ecaee3bbcad8cb2cbb Mon Sep 17 00:00:00 2001 From: jcoupey Date: Sun, 13 Aug 2023 10:49:28 +0200 Subject: [PATCH 04/10] Switch to move ctor for UndirectedGraph based on edges. --- src/algorithms/kruskal.cpp | 2 +- src/problems/tsp/heuristics/christofides.cpp | 3 ++- src/structures/generic/undirected_graph.cpp | 4 ++-- src/structures/generic/undirected_graph.h | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/algorithms/kruskal.cpp b/src/algorithms/kruskal.cpp index 54dc4806f..d8bd05cb3 100644 --- a/src/algorithms/kruskal.cpp +++ b/src/algorithms/kruskal.cpp @@ -56,7 +56,7 @@ UndirectedGraph minimum_spanning_tree(const UndirectedGraph& graph) { } } - return UndirectedGraph(mst); + return UndirectedGraph(std::move(mst)); } template UndirectedGraph diff --git a/src/problems/tsp/heuristics/christofides.cpp b/src/problems/tsp/heuristics/christofides.cpp index fc89187c8..081feff98 100644 --- a/src/problems/tsp/heuristics/christofides.cpp +++ b/src/problems/tsp/heuristics/christofides.cpp @@ -99,7 +99,8 @@ std::list christofides(const Matrix& sym_matrix) { } // Building Eulerian graph from the edges. - utils::UndirectedGraph eulerian_graph(eulerian_graph_edges); + utils::UndirectedGraph eulerian_graph( + std::move(eulerian_graph_edges)); assert(eulerian_graph.size() >= 2); // Hierholzer's algorithm: building and joining closed tours with diff --git a/src/structures/generic/undirected_graph.cpp b/src/structures/generic/undirected_graph.cpp index 4d7113b41..c4483ca6e 100644 --- a/src/structures/generic/undirected_graph.cpp +++ b/src/structures/generic/undirected_graph.cpp @@ -42,8 +42,8 @@ UndirectedGraph::UndirectedGraph(const Matrix& m) : _size(m.size()) { } template -UndirectedGraph::UndirectedGraph(std::vector> edges) - : _edges{std::move(edges)} { +UndirectedGraph::UndirectedGraph(std::vector>&& edges) + : _edges(edges) { for (auto const& edge : _edges) { Index first = edge.get_first_vertex(); Index second = edge.get_second_vertex(); diff --git a/src/structures/generic/undirected_graph.h b/src/structures/generic/undirected_graph.h index 3689375f8..4d26a59c1 100644 --- a/src/structures/generic/undirected_graph.h +++ b/src/structures/generic/undirected_graph.h @@ -31,7 +31,7 @@ template class UndirectedGraph { UndirectedGraph(const Matrix& m); - UndirectedGraph(std::vector> edges); + UndirectedGraph(std::vector>&& edges); std::size_t size() const; From 009e1598e08658b7d781a8a7b48d8ea69e9f54d8 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Sun, 13 Aug 2023 10:56:44 +0200 Subject: [PATCH 05/10] Switch to unordered_set in christofides. --- src/problems/tsp/heuristics/christofides.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/problems/tsp/heuristics/christofides.cpp b/src/problems/tsp/heuristics/christofides.cpp index 081feff98..4f2496a43 100644 --- a/src/problems/tsp/heuristics/christofides.cpp +++ b/src/problems/tsp/heuristics/christofides.cpp @@ -8,7 +8,7 @@ All rights reserved (see LICENSE). */ #include -#include +#include #include "algorithms/kruskal.h" #include "algorithms/munkres.h" @@ -86,7 +86,7 @@ std::list christofides(const Matrix& sym_matrix) { // Adding edges from minimum weight perfect matching (with the // original vertices index). Edges appear twice in matching so we // need to remember the one already added. - std::set already_added; + std::unordered_set already_added; for (const auto& edge : mwpm_final) { Index first_index = mst_odd_vertices[edge.first]; Index second_index = mst_odd_vertices[edge.second]; @@ -156,7 +156,7 @@ std::list christofides(const Matrix& sym_matrix) { } } while (!complete_tour); - std::set already_visited; + std::unordered_set already_visited; std::list tour; for (const auto& vertex : eulerian_path) { auto ret = already_visited.insert(vertex); From 22f7b452ea0b3b075dc6db00df5d6563a7969d26 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Sun, 13 Aug 2023 11:11:23 +0200 Subject: [PATCH 06/10] Add some constness. --- src/problems/tsp/heuristics/christofides.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/problems/tsp/heuristics/christofides.cpp b/src/problems/tsp/heuristics/christofides.cpp index 4f2496a43..0e74e3a6e 100644 --- a/src/problems/tsp/heuristics/christofides.cpp +++ b/src/problems/tsp/heuristics/christofides.cpp @@ -22,10 +22,10 @@ std::list christofides(const Matrix& sym_matrix) { // vertices. // Compute symmetric graph from the matrix. - auto sym_graph = utils::UndirectedGraph(sym_matrix); + const auto sym_graph = utils::UndirectedGraph(sym_matrix); // Work on a minimum spanning tree seen as a graph. - auto mst_graph = utils::minimum_spanning_tree(sym_graph); + const auto mst_graph = utils::minimum_spanning_tree(sym_graph); // Getting minimum spanning tree of associated graph under the form // of an adjacency list. From 845d3a79fd70ee93c4ff5274616df4f1fc21fc01 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Sun, 13 Aug 2023 11:32:39 +0200 Subject: [PATCH 07/10] Move job_ranks in TSP ctor. --- src/problems/cvrp/cvrp.cpp | 2 +- src/problems/tsp/tsp.cpp | 4 ++-- src/problems/tsp/tsp.h | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/problems/cvrp/cvrp.cpp b/src/problems/cvrp/cvrp.cpp index c659ab35f..28171a587 100644 --- a/src/problems/cvrp/cvrp.cpp +++ b/src/problems/cvrp/cvrp.cpp @@ -152,7 +152,7 @@ Solution CVRP::solve(unsigned exploration_level, std::vector job_ranks(_input.jobs.size()); std::iota(job_ranks.begin(), job_ranks.end(), 0); - TSP p(_input, job_ranks, 0); + TSP p(_input, std::move(job_ranks), 0); RawRoute r(_input, 0, 0); r.set_route(_input, p.raw_solve(nb_threads, timeout)); diff --git a/src/problems/tsp/tsp.cpp b/src/problems/tsp/tsp.cpp index f305f6b9a..b49288d77 100644 --- a/src/problems/tsp/tsp.cpp +++ b/src/problems/tsp/tsp.cpp @@ -36,10 +36,10 @@ UserCost compute_cost(const std::list& tour, return cost; } -TSP::TSP(const Input& input, std::vector job_ranks, Index vehicle_rank) +TSP::TSP(const Input& input, std::vector&& job_ranks, Index vehicle_rank) : VRP(input), _vehicle_rank(vehicle_rank), - _job_ranks(std::move(job_ranks)), + _job_ranks(job_ranks), _has_start(_input.vehicles[_vehicle_rank].has_start()), _has_end(_input.vehicles[_vehicle_rank].has_end()) { diff --git a/src/problems/tsp/tsp.h b/src/problems/tsp/tsp.h index dbde40074..9036dd621 100644 --- a/src/problems/tsp/tsp.h +++ b/src/problems/tsp/tsp.h @@ -22,13 +22,13 @@ using RawSolution = std::vector; class TSP : public VRP { private: - Index _vehicle_rank; + const Index _vehicle_rank; // Holds the matching from index in _matrix to rank in input::_jobs. - std::vector _job_ranks; + const std::vector _job_ranks; bool _is_symmetric{true}; - bool _has_start; + const bool _has_start; Index _start; - bool _has_end; + const bool _has_end; Index _end; Matrix _matrix; Matrix _symmetrized_matrix; @@ -39,7 +39,7 @@ class TSP : public VRP { UserCost symmetrized_cost(const std::list& tour) const; public: - TSP(const Input& input, std::vector job_ranks, Index vehicle_rank); + TSP(const Input& input, std::vector&& job_ranks, Index vehicle_rank); std::vector raw_solve(unsigned nb_threads, const Timeout& timeout) const; From 01cad4b4d1ef6533bd0f563005250cdbffc82094 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Sun, 13 Aug 2023 12:04:32 +0200 Subject: [PATCH 08/10] Reserve whenever possible in TSP local search. --- src/algorithms/kruskal.cpp | 2 + src/problems/tsp/heuristics/local_search.cpp | 51 ++++++-------------- src/problems/tsp/tsp.cpp | 2 + 3 files changed, 20 insertions(+), 35 deletions(-) diff --git a/src/algorithms/kruskal.cpp b/src/algorithms/kruskal.cpp index d8bd05cb3..d080ccd5a 100644 --- a/src/algorithms/kruskal.cpp +++ b/src/algorithms/kruskal.cpp @@ -26,6 +26,7 @@ UndirectedGraph minimum_spanning_tree(const UndirectedGraph& graph) { // Storing the edges of the minimum spanning tree. std::vector> mst; + mst.reserve(graph.size() - 1); // During Kruskal algorithm, the number of connected components will // decrease until we obtain a single component (the final tree). We @@ -55,6 +56,7 @@ UndirectedGraph minimum_spanning_tree(const UndirectedGraph& graph) { } } } + assert(mst.size() == graph.size() - 1); return UndirectedGraph(std::move(mst)); } diff --git a/src/problems/tsp/heuristics/local_search.cpp b/src/problems/tsp/heuristics/local_search.cpp index cdfecebe7..b94a08487 100644 --- a/src/problems/tsp/heuristics/local_search.cpp +++ b/src/problems/tsp/heuristics/local_search.cpp @@ -66,6 +66,7 @@ LocalSearch::LocalSearch(const Matrix& matrix, // Build a vector of bounds that easily split the [0, _edges.size()] // look-up range 'evenly' between threads for 2-opt symmetric // operator. + _sym_two_opt_rank_limits.reserve(_nb_threads + 1); _sym_two_opt_rank_limits.push_back(0); if (_nb_threads > 1) { @@ -168,10 +169,10 @@ UserCost LocalSearch::relocate_step() { std::vector best_edge_1_starts(_nb_threads); std::vector best_edge_2_starts(_nb_threads); - // Start other threads, keeping a piece of the range for the main - // thread. std::vector threads; - for (std::size_t i = 0; i < _nb_threads - 1; ++i) { + threads.reserve(_nb_threads); + + for (std::size_t i = 0; i < _nb_threads; ++i) { threads.emplace_back(look_up, _rank_limits[i], _rank_limits[i + 1], @@ -180,12 +181,6 @@ UserCost LocalSearch::relocate_step() { std::ref(best_edge_2_starts[i])); } - look_up(_rank_limits[_nb_threads - 1], - _rank_limits[_nb_threads], - std::ref(best_gains[_nb_threads - 1]), - std::ref(best_edge_1_starts[_nb_threads - 1]), - std::ref(best_edge_2_starts[_nb_threads - 1])); - for (auto& t : threads) { t.join(); } @@ -444,7 +439,9 @@ UserCost LocalSearch::two_opt_step() { // Start other threads, keeping a piece of the range for the main // thread. std::vector threads; - for (std::size_t i = 0; i < _nb_threads - 1; ++i) { + threads.reserve(_nb_threads); + + for (std::size_t i = 0; i < _nb_threads; ++i) { threads.emplace_back(look_up, _sym_two_opt_rank_limits[i], _sym_two_opt_rank_limits[i + 1], @@ -453,12 +450,6 @@ UserCost LocalSearch::two_opt_step() { std::ref(best_edge_2_starts[i])); } - look_up(_sym_two_opt_rank_limits[_nb_threads - 1], - _sym_two_opt_rank_limits[_nb_threads], - std::ref(best_gains[_nb_threads - 1]), - std::ref(best_edge_1_starts[_nb_threads - 1]), - std::ref(best_edge_2_starts[_nb_threads - 1])); - for (auto& t : threads) { t.join(); } @@ -571,7 +562,9 @@ UserCost LocalSearch::asym_two_opt_step() { // The limits in the range given to each thread are not ranks but // actual nodes used to browse a piece of the current tour. - std::vector limit_nodes{init}; + std::vector limit_nodes; + limit_nodes.reserve(_nb_threads + 1); + limit_nodes.push_back(init); Index node = init; for (std::size_t i = 0; i < _nb_threads - 1; ++i) { // Finding nodes that separate current tour in _nb_threads ranges. @@ -581,10 +574,10 @@ UserCost LocalSearch::asym_two_opt_step() { } limit_nodes.push_back(init); - // Start other threads, keeping a piece of the range for the main - // thread. std::vector threads; - for (std::size_t i = 0; i < _nb_threads - 1; ++i) { + threads.reserve(_nb_threads); + + for (std::size_t i = 0; i < _nb_threads; ++i) { threads.emplace_back(look_up, limit_nodes[i], limit_nodes[i + 1], @@ -593,12 +586,6 @@ UserCost LocalSearch::asym_two_opt_step() { std::ref(best_edge_2_starts[i])); } - look_up(limit_nodes[_nb_threads - 1], - limit_nodes[_nb_threads], - std::ref(best_gains[_nb_threads - 1]), - std::ref(best_edge_1_starts[_nb_threads - 1]), - std::ref(best_edge_2_starts[_nb_threads - 1])); - for (auto& t : threads) { t.join(); } @@ -730,10 +717,10 @@ UserCost LocalSearch::or_opt_step() { std::vector best_edge_1_starts(_nb_threads); std::vector best_edge_2_starts(_nb_threads); - // Start other threads, keeping a piece of the range for the main - // thread. std::vector threads; - for (std::size_t i = 0; i < _nb_threads - 1; ++i) { + threads.reserve(_nb_threads); + + for (std::size_t i = 0; i < _nb_threads; ++i) { threads.emplace_back(look_up, _rank_limits[i], _rank_limits[i + 1], @@ -742,12 +729,6 @@ UserCost LocalSearch::or_opt_step() { std::ref(best_edge_2_starts[i])); } - look_up(_rank_limits[_nb_threads - 1], - _rank_limits[_nb_threads], - std::ref(best_gains[_nb_threads - 1]), - std::ref(best_edge_1_starts[_nb_threads - 1]), - std::ref(best_edge_2_starts[_nb_threads - 1])); - for (auto& t : threads) { t.join(); } diff --git a/src/problems/tsp/tsp.cpp b/src/problems/tsp/tsp.cpp index b49288d77..31ca2a962 100644 --- a/src/problems/tsp/tsp.cpp +++ b/src/problems/tsp/tsp.cpp @@ -47,6 +47,8 @@ TSP::TSP(const Input& input, std::vector&& job_ranks, Index vehicle_rank) // Pick ranks to select from input matrix. std::vector matrix_ranks; + matrix_ranks.reserve(_job_ranks.size() + 2); + std::transform(_job_ranks.cbegin(), _job_ranks.cend(), std::back_inserter(matrix_ranks), From 22986f2fe1e159e0913324394e5301074d71921c Mon Sep 17 00:00:00 2001 From: jcoupey Date: Sun, 13 Aug 2023 12:19:15 +0200 Subject: [PATCH 09/10] Mention reserve refactor in changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 906d88447..f552c9387 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - Add job id to error message for unreachable step (#946) - Reduce `compute_best_route_split_choice` complexity (#962) - `Eval::operator<` sorts on cost, then duration (#914) +- Reserve `vector` capacity whenever possible (#915) ### Fixed From f6090291f0e05ec558107603383153c57349d792 Mon Sep 17 00:00:00 2001 From: jcoupey Date: Mon, 14 Aug 2023 10:45:03 +0200 Subject: [PATCH 10/10] Fixes during review. --- src/algorithms/heuristics/heuristics.cpp | 6 +++--- src/algorithms/local_search/insertion_search.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/algorithms/heuristics/heuristics.cpp b/src/algorithms/heuristics/heuristics.cpp index db2fadeb4..8d30b62f3 100644 --- a/src/algorithms/heuristics/heuristics.cpp +++ b/src/algorithms/heuristics/heuristics.cpp @@ -349,7 +349,7 @@ T basic(const Input& input, INIT init, double lambda, SORT sort) { // Build replacement sequence for current insertion. std::vector modified_with_pd; - modified_with_pd.reserve(current_r.size() - pickup_r + 3); + modified_with_pd.reserve(current_r.size() - pickup_r + 2); modified_with_pd.push_back(job_rank); Amount modified_delivery = input.zero_amount(); @@ -788,7 +788,7 @@ T dynamic_vehicle_choice(const Input& input, // Build replacement sequence for current insertion. std::vector modified_with_pd; - modified_with_pd.reserve(current_r.size() - pickup_r + 3); + modified_with_pd.reserve(current_r.size() - pickup_r + 2); modified_with_pd.push_back(job_rank); Amount modified_delivery = input.zero_amount(); @@ -872,7 +872,7 @@ T dynamic_vehicle_choice(const Input& input, keep_going = true; } if (input.jobs[best_job_rank].type == JOB_TYPE::PICKUP) { - std::vector modified_with_pd({best_job_rank}); + std::vector modified_with_pd; modified_with_pd.reserve(best_delivery_r - best_pickup_r + 2); modified_with_pd.push_back(best_job_rank); diff --git a/src/algorithms/local_search/insertion_search.h b/src/algorithms/local_search/insertion_search.h index c0ce7ed6a..809a6e989 100644 --- a/src/algorithms/local_search/insertion_search.h +++ b/src/algorithms/local_search/insertion_search.h @@ -138,7 +138,7 @@ RouteInsertion compute_best_insertion_pd(const Input& input, // Build replacement sequence for current insertion. std::vector modified_with_pd; - if (pickup_r < end_d_rank) { + if (pickup_r <= end_d_rank) { modified_with_pd.reserve(end_d_rank - pickup_r + 2); } modified_with_pd.push_back(j);