cpu_assignment_test.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cpumanager
  14. import (
  15. "reflect"
  16. "testing"
  17. "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology"
  18. "k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
  19. )
  20. func TestCPUAccumulatorFreeSockets(t *testing.T) {
  21. testCases := []struct {
  22. description string
  23. topo *topology.CPUTopology
  24. availableCPUs cpuset.CPUSet
  25. expect []int
  26. }{
  27. {
  28. "single socket HT, 1 socket free",
  29. topoSingleSocketHT,
  30. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  31. []int{0},
  32. },
  33. {
  34. "single socket HT, 0 sockets free",
  35. topoSingleSocketHT,
  36. cpuset.NewCPUSet(1, 2, 3, 4, 5, 6, 7),
  37. []int{},
  38. },
  39. {
  40. "dual socket HT, 2 sockets free",
  41. topoDualSocketHT,
  42. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
  43. []int{0, 1},
  44. },
  45. {
  46. "dual socket HT, 1 socket free",
  47. topoDualSocketHT,
  48. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11),
  49. []int{1},
  50. },
  51. {
  52. "dual socket HT, 0 sockets free",
  53. topoDualSocketHT,
  54. cpuset.NewCPUSet(0, 2, 3, 4, 5, 6, 7, 8, 9, 11),
  55. []int{},
  56. },
  57. }
  58. for _, tc := range testCases {
  59. acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0)
  60. result := acc.freeSockets()
  61. if !reflect.DeepEqual(result, tc.expect) {
  62. t.Errorf("[%s] expected %v to equal %v", tc.description, result, tc.expect)
  63. }
  64. }
  65. }
  66. func TestCPUAccumulatorFreeCores(t *testing.T) {
  67. testCases := []struct {
  68. description string
  69. topo *topology.CPUTopology
  70. availableCPUs cpuset.CPUSet
  71. expect []int
  72. }{
  73. {
  74. "single socket HT, 4 cores free",
  75. topoSingleSocketHT,
  76. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  77. []int{0, 1, 2, 3},
  78. },
  79. {
  80. "single socket HT, 3 cores free",
  81. topoSingleSocketHT,
  82. cpuset.NewCPUSet(0, 1, 2, 4, 5, 6),
  83. []int{0, 1, 2},
  84. },
  85. {
  86. "single socket HT, 3 cores free (1 partially consumed)",
  87. topoSingleSocketHT,
  88. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6),
  89. []int{0, 1, 2},
  90. },
  91. {
  92. "single socket HT, 0 cores free",
  93. topoSingleSocketHT,
  94. cpuset.NewCPUSet(),
  95. []int{},
  96. },
  97. {
  98. "single socket HT, 0 cores free (4 partially consumed)",
  99. topoSingleSocketHT,
  100. cpuset.NewCPUSet(0, 1, 2, 3),
  101. []int{},
  102. },
  103. {
  104. "dual socket HT, 6 cores free",
  105. topoDualSocketHT,
  106. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
  107. []int{0, 2, 4, 1, 3, 5},
  108. },
  109. {
  110. "dual socket HT, 5 cores free (1 consumed from socket 0)",
  111. topoDualSocketHT,
  112. cpuset.NewCPUSet(2, 1, 3, 4, 5, 7, 8, 9, 10, 11),
  113. []int{2, 4, 1, 3, 5},
  114. },
  115. {
  116. "dual socket HT, 4 cores free (1 consumed from each socket)",
  117. topoDualSocketHT,
  118. cpuset.NewCPUSet(2, 3, 4, 5, 8, 9, 10, 11),
  119. []int{2, 4, 3, 5},
  120. },
  121. }
  122. for _, tc := range testCases {
  123. acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0)
  124. result := acc.freeCores()
  125. if !reflect.DeepEqual(result, tc.expect) {
  126. t.Errorf("[%s] expected %v to equal %v", tc.description, result, tc.expect)
  127. }
  128. }
  129. }
  130. func TestCPUAccumulatorFreeCPUs(t *testing.T) {
  131. testCases := []struct {
  132. description string
  133. topo *topology.CPUTopology
  134. availableCPUs cpuset.CPUSet
  135. expect []int
  136. }{
  137. {
  138. "single socket HT, 8 cpus free",
  139. topoSingleSocketHT,
  140. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  141. []int{0, 4, 1, 5, 2, 6, 3, 7},
  142. },
  143. {
  144. "single socket HT, 5 cpus free",
  145. topoSingleSocketHT,
  146. cpuset.NewCPUSet(3, 4, 5, 6, 7),
  147. []int{4, 5, 6, 3, 7},
  148. },
  149. {
  150. "dual socket HT, 12 cpus free",
  151. topoDualSocketHT,
  152. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
  153. []int{0, 6, 2, 8, 4, 10, 1, 7, 3, 9, 5, 11},
  154. },
  155. {
  156. "dual socket HT, 11 cpus free",
  157. topoDualSocketHT,
  158. cpuset.NewCPUSet(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
  159. []int{6, 2, 8, 4, 10, 1, 7, 3, 9, 5, 11},
  160. },
  161. {
  162. "dual socket HT, 10 cpus free",
  163. topoDualSocketHT,
  164. cpuset.NewCPUSet(1, 2, 3, 4, 5, 7, 8, 9, 10, 11),
  165. []int{2, 8, 4, 10, 1, 7, 3, 9, 5, 11},
  166. },
  167. }
  168. for _, tc := range testCases {
  169. acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0)
  170. result := acc.freeCPUs()
  171. if !reflect.DeepEqual(result, tc.expect) {
  172. t.Errorf("[%s] expected %v to equal %v", tc.description, result, tc.expect)
  173. }
  174. }
  175. }
  176. func TestCPUAccumulatorTake(t *testing.T) {
  177. testCases := []struct {
  178. description string
  179. topo *topology.CPUTopology
  180. availableCPUs cpuset.CPUSet
  181. takeCPUs []cpuset.CPUSet
  182. numCPUs int
  183. expectSatisfied bool
  184. expectFailed bool
  185. }{
  186. {
  187. "take 0 cpus from a single socket HT, require 1",
  188. topoSingleSocketHT,
  189. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  190. []cpuset.CPUSet{cpuset.NewCPUSet()},
  191. 1,
  192. false,
  193. false,
  194. },
  195. {
  196. "take 0 cpus from a single socket HT, require 1, none available",
  197. topoSingleSocketHT,
  198. cpuset.NewCPUSet(),
  199. []cpuset.CPUSet{cpuset.NewCPUSet()},
  200. 1,
  201. false,
  202. true,
  203. },
  204. {
  205. "take 1 cpu from a single socket HT, require 1",
  206. topoSingleSocketHT,
  207. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  208. []cpuset.CPUSet{cpuset.NewCPUSet(0)},
  209. 1,
  210. true,
  211. false,
  212. },
  213. {
  214. "take 1 cpu from a single socket HT, require 2",
  215. topoSingleSocketHT,
  216. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  217. []cpuset.CPUSet{cpuset.NewCPUSet(0)},
  218. 2,
  219. false,
  220. false,
  221. },
  222. {
  223. "take 2 cpu from a single socket HT, require 4, expect failed",
  224. topoSingleSocketHT,
  225. cpuset.NewCPUSet(0, 1, 2),
  226. []cpuset.CPUSet{cpuset.NewCPUSet(0), cpuset.NewCPUSet(1)},
  227. 4,
  228. false,
  229. true,
  230. },
  231. {
  232. "take all cpus one at a time from a single socket HT, require 8",
  233. topoSingleSocketHT,
  234. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  235. []cpuset.CPUSet{
  236. cpuset.NewCPUSet(0),
  237. cpuset.NewCPUSet(1),
  238. cpuset.NewCPUSet(2),
  239. cpuset.NewCPUSet(3),
  240. cpuset.NewCPUSet(4),
  241. cpuset.NewCPUSet(5),
  242. cpuset.NewCPUSet(6),
  243. cpuset.NewCPUSet(7),
  244. },
  245. 8,
  246. true,
  247. false,
  248. },
  249. }
  250. for _, tc := range testCases {
  251. acc := newCPUAccumulator(tc.topo, tc.availableCPUs, tc.numCPUs)
  252. totalTaken := 0
  253. for _, cpus := range tc.takeCPUs {
  254. acc.take(cpus)
  255. totalTaken += cpus.Size()
  256. }
  257. if tc.expectSatisfied != acc.isSatisfied() {
  258. t.Errorf("[%s] expected acc.isSatisfied() to be %t", tc.description, tc.expectSatisfied)
  259. }
  260. if tc.expectFailed != acc.isFailed() {
  261. t.Errorf("[%s] expected acc.isFailed() to be %t", tc.description, tc.expectFailed)
  262. }
  263. for _, cpus := range tc.takeCPUs {
  264. availableCPUs := acc.details.CPUs()
  265. if cpus.Intersection(availableCPUs).Size() > 0 {
  266. t.Errorf("[%s] expected intersection of taken cpus [%s] and acc.details.CPUs() [%s] to be empty", tc.description, cpus, availableCPUs)
  267. }
  268. if !cpus.IsSubsetOf(acc.result) {
  269. t.Errorf("[%s] expected [%s] to be a subset of acc.result [%s]", tc.description, cpus, acc.result)
  270. }
  271. }
  272. expNumCPUsNeeded := tc.numCPUs - totalTaken
  273. if acc.numCPUsNeeded != expNumCPUsNeeded {
  274. t.Errorf("[%s] expected acc.numCPUsNeeded to be %d (got %d)", tc.description, expNumCPUsNeeded, acc.numCPUsNeeded)
  275. }
  276. }
  277. }
  278. func TestTakeByTopology(t *testing.T) {
  279. testCases := []struct {
  280. description string
  281. topo *topology.CPUTopology
  282. availableCPUs cpuset.CPUSet
  283. numCPUs int
  284. expErr string
  285. expResult cpuset.CPUSet
  286. }{
  287. {
  288. "take more cpus than are available from single socket with HT",
  289. topoSingleSocketHT,
  290. cpuset.NewCPUSet(0, 2, 4, 6),
  291. 5,
  292. "not enough cpus available to satisfy request",
  293. cpuset.NewCPUSet(),
  294. },
  295. {
  296. "take zero cpus from single socket with HT",
  297. topoSingleSocketHT,
  298. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  299. 0,
  300. "",
  301. cpuset.NewCPUSet(),
  302. },
  303. {
  304. "take one cpu from single socket with HT",
  305. topoSingleSocketHT,
  306. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  307. 1,
  308. "",
  309. cpuset.NewCPUSet(0),
  310. },
  311. {
  312. "take one cpu from single socket with HT, some cpus are taken",
  313. topoSingleSocketHT,
  314. cpuset.NewCPUSet(1, 3, 5, 6, 7),
  315. 1,
  316. "",
  317. cpuset.NewCPUSet(6),
  318. },
  319. {
  320. "take two cpus from single socket with HT",
  321. topoSingleSocketHT,
  322. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  323. 2,
  324. "",
  325. cpuset.NewCPUSet(0, 4),
  326. },
  327. {
  328. "take all cpus from single socket with HT",
  329. topoSingleSocketHT,
  330. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  331. 8,
  332. "",
  333. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  334. },
  335. {
  336. "take two cpus from single socket with HT, only one core totally free",
  337. topoSingleSocketHT,
  338. cpuset.NewCPUSet(0, 1, 2, 3, 6),
  339. 2,
  340. "",
  341. cpuset.NewCPUSet(2, 6),
  342. },
  343. {
  344. "take one cpu from dual socket with HT - core from Socket 0",
  345. topoDualSocketHT,
  346. cpuset.NewCPUSet(1, 2, 3, 4, 5, 7, 8, 9, 10, 11),
  347. 1,
  348. "",
  349. cpuset.NewCPUSet(2),
  350. },
  351. {
  352. "take a socket of cpus from dual socket with HT",
  353. topoDualSocketHT,
  354. cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
  355. 6,
  356. "",
  357. cpuset.NewCPUSet(0, 2, 4, 6, 8, 10),
  358. },
  359. }
  360. for _, tc := range testCases {
  361. result, err := takeByTopology(tc.topo, tc.availableCPUs, tc.numCPUs)
  362. if tc.expErr != "" && err.Error() != tc.expErr {
  363. t.Errorf("expected error to be [%v] but it was [%v] in test \"%s\"", tc.expErr, err, tc.description)
  364. }
  365. if !result.Equals(tc.expResult) {
  366. t.Errorf("expected result [%s] to equal [%s] in test \"%s\"", result, tc.expResult, tc.description)
  367. }
  368. }
  369. }