| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
- #include "cmCTestBinPacker.h"
- #include <algorithm>
- #include <utility>
- bool cmCTestBinPackerAllocation::operator==(
- const cmCTestBinPackerAllocation& other) const
- {
- return this->ProcessIndex == other.ProcessIndex &&
- this->SlotsNeeded == other.SlotsNeeded && this->Id == other.Id;
- }
- bool cmCTestBinPackerAllocation::operator!=(
- const cmCTestBinPackerAllocation& other) const
- {
- return !(*this == other);
- }
- namespace {
- /*
- * The following algorithm is used to do two things:
- *
- * 1) Determine if a test's resource requirements can fit within the resources
- * present on the system, and
- * 2) Do the actual allocation
- *
- * This algorithm performs a recursive search, looking for a bin pack that will
- * fit the specified requirements. It has a template to specify different
- * optimization strategies. If it ever runs out of room, it backtracks as far
- * down the stack as it needs to and tries a different combination until no
- * more combinations can be tried.
- */
- template <typename AllocationStrategy>
- static bool AllocateCTestResources(
- const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
- const std::vector<std::string>& resourcesSorted, std::size_t currentIndex,
- std::vector<cmCTestBinPackerAllocation*>& allocations)
- {
- // Iterate through all large enough resources until we find a solution
- std::size_t resourceIndex = 0;
- while (resourceIndex < resourcesSorted.size()) {
- auto const& resource = resources.at(resourcesSorted[resourceIndex]);
- if (resource.Free() >=
- static_cast<unsigned int>(allocations[currentIndex]->SlotsNeeded)) {
- // Preemptively allocate the resource
- allocations[currentIndex]->Id = resourcesSorted[resourceIndex];
- if (currentIndex + 1 >= allocations.size()) {
- // We have a solution
- return true;
- }
- // Move the resource up the list until it is sorted again
- auto resources2 = resources;
- auto resourcesSorted2 = resourcesSorted;
- resources2[resourcesSorted2[resourceIndex]].Locked +=
- allocations[currentIndex]->SlotsNeeded;
- AllocationStrategy::IncrementalSort(resources2, resourcesSorted2,
- resourceIndex);
- // Recurse one level deeper
- if (AllocateCTestResources<AllocationStrategy>(
- resources2, resourcesSorted2, currentIndex + 1, allocations)) {
- return true;
- }
- }
- // No solution found here, deallocate the resource and try the next one
- allocations[currentIndex]->Id.clear();
- auto freeSlots = resources.at(resourcesSorted.at(resourceIndex)).Free();
- do {
- ++resourceIndex;
- } while (resourceIndex < resourcesSorted.size() &&
- resources.at(resourcesSorted.at(resourceIndex)).Free() ==
- freeSlots);
- }
- // No solution was found
- return false;
- }
- template <typename AllocationStrategy>
- static bool AllocateCTestResources(
- const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
- std::vector<cmCTestBinPackerAllocation>& allocations)
- {
- // Sort the resource requirements in descending order by slots needed
- std::vector<cmCTestBinPackerAllocation*> allocationsPtr;
- allocationsPtr.reserve(allocations.size());
- for (auto& allocation : allocations) {
- allocationsPtr.push_back(&allocation);
- }
- std::stable_sort(
- allocationsPtr.rbegin(), allocationsPtr.rend(),
- [](cmCTestBinPackerAllocation* a1, cmCTestBinPackerAllocation* a2) {
- return a1->SlotsNeeded < a2->SlotsNeeded;
- });
- // Sort the resources according to sort strategy
- std::vector<std::string> resourcesSorted;
- resourcesSorted.reserve(resources.size());
- for (auto const& res : resources) {
- resourcesSorted.push_back(res.first);
- }
- AllocationStrategy::InitialSort(resources, resourcesSorted);
- // Do the actual allocation
- return AllocateCTestResources<AllocationStrategy>(
- resources, resourcesSorted, std::size_t(0), allocationsPtr);
- }
- class RoundRobinAllocationStrategy
- {
- public:
- static void InitialSort(
- const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
- std::vector<std::string>& resourcesSorted);
- static void IncrementalSort(
- const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
- std::vector<std::string>& resourcesSorted, std::size_t lastAllocatedIndex);
- };
- void RoundRobinAllocationStrategy::InitialSort(
- const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
- std::vector<std::string>& resourcesSorted)
- {
- std::stable_sort(
- resourcesSorted.rbegin(), resourcesSorted.rend(),
- [&resources](const std::string& id1, const std::string& id2) {
- return resources.at(id1).Free() < resources.at(id2).Free();
- });
- }
- void RoundRobinAllocationStrategy::IncrementalSort(
- const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
- std::vector<std::string>& resourcesSorted, std::size_t lastAllocatedIndex)
- {
- auto tmp = resourcesSorted[lastAllocatedIndex];
- std::size_t i = lastAllocatedIndex;
- while (i < resourcesSorted.size() - 1 &&
- resources.at(resourcesSorted[i + 1]).Free() >
- resources.at(tmp).Free()) {
- resourcesSorted[i] = resourcesSorted[i + 1];
- ++i;
- }
- resourcesSorted[i] = tmp;
- }
- class BlockAllocationStrategy
- {
- public:
- static void InitialSort(
- const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
- std::vector<std::string>& resourcesSorted);
- static void IncrementalSort(
- const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
- std::vector<std::string>& resourcesSorted, std::size_t lastAllocatedIndex);
- };
- void BlockAllocationStrategy::InitialSort(
- const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
- std::vector<std::string>& resourcesSorted)
- {
- std::stable_sort(
- resourcesSorted.rbegin(), resourcesSorted.rend(),
- [&resources](const std::string& id1, const std::string& id2) {
- return resources.at(id1).Free() < resources.at(id2).Free();
- });
- }
- void BlockAllocationStrategy::IncrementalSort(
- const std::map<std::string, cmCTestResourceAllocator::Resource>&,
- std::vector<std::string>& resourcesSorted, std::size_t lastAllocatedIndex)
- {
- auto tmp = resourcesSorted[lastAllocatedIndex];
- std::size_t i = lastAllocatedIndex;
- while (i > 0) {
- resourcesSorted[i] = resourcesSorted[i - 1];
- --i;
- }
- resourcesSorted[i] = tmp;
- }
- }
- bool cmAllocateCTestResourcesRoundRobin(
- const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
- std::vector<cmCTestBinPackerAllocation>& allocations)
- {
- return AllocateCTestResources<RoundRobinAllocationStrategy>(resources,
- allocations);
- }
- bool cmAllocateCTestResourcesBlock(
- const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
- std::vector<cmCTestBinPackerAllocation>& allocations)
- {
- return AllocateCTestResources<BlockAllocationStrategy>(resources,
- allocations);
- }
|