cmCTestBinPacker.cxx 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmCTestBinPacker.h"
  4. #include <algorithm>
  5. #include <utility>
  6. bool cmCTestBinPackerAllocation::operator==(
  7. const cmCTestBinPackerAllocation& other) const
  8. {
  9. return this->ProcessIndex == other.ProcessIndex &&
  10. this->SlotsNeeded == other.SlotsNeeded && this->Id == other.Id;
  11. }
  12. bool cmCTestBinPackerAllocation::operator!=(
  13. const cmCTestBinPackerAllocation& other) const
  14. {
  15. return !(*this == other);
  16. }
  17. namespace {
  18. /*
  19. * The following algorithm is used to do two things:
  20. *
  21. * 1) Determine if a test's resource requirements can fit within the resources
  22. * present on the system, and
  23. * 2) Do the actual allocation
  24. *
  25. * This algorithm performs a recursive search, looking for a bin pack that will
  26. * fit the specified requirements. It has a template to specify different
  27. * optimization strategies. If it ever runs out of room, it backtracks as far
  28. * down the stack as it needs to and tries a different combination until no
  29. * more combinations can be tried.
  30. */
  31. template <typename AllocationStrategy>
  32. static bool AllocateCTestResources(
  33. const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
  34. const std::vector<std::string>& resourcesSorted, std::size_t currentIndex,
  35. std::vector<cmCTestBinPackerAllocation*>& allocations)
  36. {
  37. // Iterate through all large enough resources until we find a solution
  38. std::size_t resourceIndex = 0;
  39. while (resourceIndex < resourcesSorted.size()) {
  40. auto const& resource = resources.at(resourcesSorted[resourceIndex]);
  41. if (resource.Free() >=
  42. static_cast<unsigned int>(allocations[currentIndex]->SlotsNeeded)) {
  43. // Preemptively allocate the resource
  44. allocations[currentIndex]->Id = resourcesSorted[resourceIndex];
  45. if (currentIndex + 1 >= allocations.size()) {
  46. // We have a solution
  47. return true;
  48. }
  49. // Move the resource up the list until it is sorted again
  50. auto resources2 = resources;
  51. auto resourcesSorted2 = resourcesSorted;
  52. resources2[resourcesSorted2[resourceIndex]].Locked +=
  53. allocations[currentIndex]->SlotsNeeded;
  54. AllocationStrategy::IncrementalSort(resources2, resourcesSorted2,
  55. resourceIndex);
  56. // Recurse one level deeper
  57. if (AllocateCTestResources<AllocationStrategy>(
  58. resources2, resourcesSorted2, currentIndex + 1, allocations)) {
  59. return true;
  60. }
  61. }
  62. // No solution found here, deallocate the resource and try the next one
  63. allocations[currentIndex]->Id.clear();
  64. auto freeSlots = resources.at(resourcesSorted.at(resourceIndex)).Free();
  65. do {
  66. ++resourceIndex;
  67. } while (resourceIndex < resourcesSorted.size() &&
  68. resources.at(resourcesSorted.at(resourceIndex)).Free() ==
  69. freeSlots);
  70. }
  71. // No solution was found
  72. return false;
  73. }
  74. template <typename AllocationStrategy>
  75. static bool AllocateCTestResources(
  76. const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
  77. std::vector<cmCTestBinPackerAllocation>& allocations)
  78. {
  79. // Sort the resource requirements in descending order by slots needed
  80. std::vector<cmCTestBinPackerAllocation*> allocationsPtr;
  81. allocationsPtr.reserve(allocations.size());
  82. for (auto& allocation : allocations) {
  83. allocationsPtr.push_back(&allocation);
  84. }
  85. std::stable_sort(
  86. allocationsPtr.rbegin(), allocationsPtr.rend(),
  87. [](cmCTestBinPackerAllocation* a1, cmCTestBinPackerAllocation* a2) {
  88. return a1->SlotsNeeded < a2->SlotsNeeded;
  89. });
  90. // Sort the resources according to sort strategy
  91. std::vector<std::string> resourcesSorted;
  92. resourcesSorted.reserve(resources.size());
  93. for (auto const& res : resources) {
  94. resourcesSorted.push_back(res.first);
  95. }
  96. AllocationStrategy::InitialSort(resources, resourcesSorted);
  97. // Do the actual allocation
  98. return AllocateCTestResources<AllocationStrategy>(
  99. resources, resourcesSorted, std::size_t(0), allocationsPtr);
  100. }
  101. class RoundRobinAllocationStrategy
  102. {
  103. public:
  104. static void InitialSort(
  105. const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
  106. std::vector<std::string>& resourcesSorted);
  107. static void IncrementalSort(
  108. const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
  109. std::vector<std::string>& resourcesSorted, std::size_t lastAllocatedIndex);
  110. };
  111. void RoundRobinAllocationStrategy::InitialSort(
  112. const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
  113. std::vector<std::string>& resourcesSorted)
  114. {
  115. std::stable_sort(
  116. resourcesSorted.rbegin(), resourcesSorted.rend(),
  117. [&resources](const std::string& id1, const std::string& id2) {
  118. return resources.at(id1).Free() < resources.at(id2).Free();
  119. });
  120. }
  121. void RoundRobinAllocationStrategy::IncrementalSort(
  122. const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
  123. std::vector<std::string>& resourcesSorted, std::size_t lastAllocatedIndex)
  124. {
  125. auto tmp = resourcesSorted[lastAllocatedIndex];
  126. std::size_t i = lastAllocatedIndex;
  127. while (i < resourcesSorted.size() - 1 &&
  128. resources.at(resourcesSorted[i + 1]).Free() >
  129. resources.at(tmp).Free()) {
  130. resourcesSorted[i] = resourcesSorted[i + 1];
  131. ++i;
  132. }
  133. resourcesSorted[i] = tmp;
  134. }
  135. class BlockAllocationStrategy
  136. {
  137. public:
  138. static void InitialSort(
  139. const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
  140. std::vector<std::string>& resourcesSorted);
  141. static void IncrementalSort(
  142. const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
  143. std::vector<std::string>& resourcesSorted, std::size_t lastAllocatedIndex);
  144. };
  145. void BlockAllocationStrategy::InitialSort(
  146. const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
  147. std::vector<std::string>& resourcesSorted)
  148. {
  149. std::stable_sort(
  150. resourcesSorted.rbegin(), resourcesSorted.rend(),
  151. [&resources](const std::string& id1, const std::string& id2) {
  152. return resources.at(id1).Free() < resources.at(id2).Free();
  153. });
  154. }
  155. void BlockAllocationStrategy::IncrementalSort(
  156. const std::map<std::string, cmCTestResourceAllocator::Resource>&,
  157. std::vector<std::string>& resourcesSorted, std::size_t lastAllocatedIndex)
  158. {
  159. auto tmp = resourcesSorted[lastAllocatedIndex];
  160. std::size_t i = lastAllocatedIndex;
  161. while (i > 0) {
  162. resourcesSorted[i] = resourcesSorted[i - 1];
  163. --i;
  164. }
  165. resourcesSorted[i] = tmp;
  166. }
  167. }
  168. bool cmAllocateCTestResourcesRoundRobin(
  169. const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
  170. std::vector<cmCTestBinPackerAllocation>& allocations)
  171. {
  172. return AllocateCTestResources<RoundRobinAllocationStrategy>(resources,
  173. allocations);
  174. }
  175. bool cmAllocateCTestResourcesBlock(
  176. const std::map<std::string, cmCTestResourceAllocator::Resource>& resources,
  177. std::vector<cmCTestBinPackerAllocation>& allocations)
  178. {
  179. return AllocateCTestResources<BlockAllocationStrategy>(resources,
  180. allocations);
  181. }