pbs_node_buckets_perf.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. # coding: utf-8
  2. # Copyright (C) 1994-2018 Altair Engineering, Inc.
  3. # For more information, contact Altair at www.altair.com.
  4. #
  5. # This file is part of the PBS Professional ("PBS Pro") software.
  6. #
  7. # Open Source License Information:
  8. #
  9. # PBS Pro is free software. You can redistribute it and/or modify it under the
  10. # terms of the GNU Affero General Public License as published by the Free
  11. # Software Foundation, either version 3 of the License, or (at your option) any
  12. # later version.
  13. #
  14. # PBS Pro is distributed in the hope that it will be useful, but WITHOUT ANY
  15. # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  16. # FOR A PARTICULAR PURPOSE.
  17. # See the GNU Affero General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU Affero General Public License
  20. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. #
  22. # Commercial License Information:
  23. #
  24. # For a copy of the commercial license terms and conditions,
  25. # go to: (http://www.pbspro.com/UserArea/agreement.html)
  26. # or contact the Altair Legal Department.
  27. #
  28. # Altair’s dual-license business model allows companies, individuals, and
  29. # organizations to create proprietary derivative works of PBS Pro and
  30. # distribute them - whether embedded or bundled with other software -
  31. # under a commercial license agreement.
  32. #
  33. # Use of Altair’s trademarks, including but not limited to "PBS™",
  34. # "PBS Professional®", and "PBS Pro™" and Altair’s logos is subject to Altair's
  35. # trademark licensing policies.
  36. from tests.performance import *
  37. class TestNodeBucketPerf(TestPerformance):
  38. """
  39. Test the performance of node buckets
  40. """
  41. def setUp(self):
  42. TestPerformance.setUp(self)
  43. self.server.manager(MGR_CMD_CREATE, RSC,
  44. {'type': 'string', 'flag': 'h'}, id='color',
  45. expect=True)
  46. self.colors = \
  47. ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']
  48. a = {'resources_available.ncpus': 1, 'resources_available.mem': '8gb'}
  49. # 10010 nodes since it divides into 7 evenly.
  50. # Each node bucket will have 1430 nodes in it
  51. self.server.create_vnodes('vnode', a, 10010, self.mom,
  52. sharednode=False,
  53. attrfunc=self.cust_attr_func, expect=False)
  54. self.server.expect(NODE, {'state=free': (GE, 10010)})
  55. self.scheduler.add_resource('color')
  56. a = {'PBS_LOG_HIGHRES_TIMESTAMP': 1}
  57. self.du.set_pbs_config(confs=a, append=True)
  58. self.scheduler.restart()
  59. def cust_attr_func(self, name, totalnodes, numnode, attribs):
  60. """
  61. Add custom resources to nodes
  62. """
  63. a = {'resources_available.color': self.colors[numnode % 7]}
  64. return dict(attribs.items() + a.items())
  65. def submit_jobs(self, attribs, num):
  66. """
  67. Submit num jobs each in their individual equiv class
  68. """
  69. wt = 100
  70. jids = []
  71. self.server.manager(MGR_CMD_SET, MGR_OBJ_SERVER,
  72. {'scheduling': 'False'}, expect=True)
  73. for i in range(num):
  74. attribs['Resource_List.walltime'] = wt + i
  75. J = Job(TEST_USER, attrs=attribs)
  76. jid = self.server.submit(J)
  77. jids.append(jid)
  78. return jids
  79. def run_cycle(self):
  80. """
  81. Run a cycle and return the length of the cycle
  82. """
  83. self.server.expect(SERVER, {'server_state': 'Scheduling'}, op=NE)
  84. self.server.manager(MGR_CMD_SET, MGR_OBJ_SERVER,
  85. {'scheduling': 'True'})
  86. self.server.expect(SERVER, {'server_state': 'Scheduling'})
  87. self.server.manager(MGR_CMD_SET, MGR_OBJ_SERVER,
  88. {'scheduling': 'False'}, expect=True)
  89. # 600 * 2sec = 20m which is the max cycle length
  90. self.server.expect(SERVER, {'server_state': 'Scheduling'}, op=NE,
  91. max_attempts=600, interval=2)
  92. c = self.scheduler.cycles(lastN=1)[0]
  93. return c.end - c.start
  94. def compare_normal_path_to_buckets(self, place, num_jobs):
  95. """
  96. Submit num_jobs jobs and run two cycles. First one with the normal
  97. node search code path and the second with buckets. Print the
  98. time difference between the two cycles.
  99. """
  100. # Submit one job to eat up the resources. We want to compare the
  101. # time it takes for the scheduler to attempt and fail to run the jobs
  102. a = {'Resource_List.select': '1429:ncpus=1:color=yellow',
  103. 'Resource_List.place': place,
  104. 'Resource_List.walltime': '1:00:00'}
  105. Jyellow = Job(TEST_USER, attrs=a)
  106. Jyellow.set_sleep_time(3600)
  107. jid_yellow = self.server.submit(Jyellow)
  108. self.server.expect(JOB, {'job_state': 'R'}, id=jid_yellow)
  109. self.server.manager(MGR_CMD_SET, MGR_OBJ_SERVER,
  110. {'scheduling': 'False'}, expect=True)
  111. # Shared jobs use standard code path
  112. a = {'Resource_List.select':
  113. '1429:ncpus=1:color=blue+1429:ncpus=1:color=yellow',
  114. "Resource_List.place": place}
  115. jids = self.submit_jobs(a, num_jobs)
  116. cycle1_time = self.run_cycle()
  117. # Excl jobs use bucket codepath
  118. a = {'Resource_List.place': place + ':excl'}
  119. for jid in jids:
  120. self.server.alterjob(jid, a)
  121. cycle2_time = self.run_cycle()
  122. log_msg = 'Cycle 1: %.2f Cycle 2: %.2f Cycle time difference: %.2f'
  123. self.logger.info(log_msg % (cycle1_time, cycle2_time,
  124. cycle1_time - cycle2_time))
  125. self.assertGreater(cycle1_time, cycle2_time)
  126. @timeout(10000)
  127. def test_node_bucket_perf_scatter(self):
  128. """
  129. Submit a large number of jobs which use node buckets. Run a cycle and
  130. compare that to a cycle that doesn't use node buckets. Jobs require
  131. place=excl to use node buckets.
  132. This test uses place=scatter. Scatter placement is quicker than free
  133. """
  134. num_jobs = 3000
  135. self.compare_normal_path_to_buckets('scatter', num_jobs)
  136. @timeout(10000)
  137. def test_node_bucket_perf_free(self):
  138. """
  139. Submit a large number of jobs which use node buckets. Run a cycle and
  140. compare that to a cycle that doesn't use node buckets. Jobs require
  141. place=excl to use node buckets.
  142. This test uses free placement. Free placement is slower than scatter
  143. """
  144. num_jobs = 3000
  145. self.compare_normal_path_to_buckets('free', num_jobs)