k8s.io/kubernetes@v1.29.3/pkg/kubelet/cm/cpumanager/cpu_assignment_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cpumanager 18 19 import ( 20 "reflect" 21 "sort" 22 "testing" 23 24 "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology" 25 "k8s.io/utils/cpuset" 26 ) 27 28 func TestCPUAccumulatorFreeSockets(t *testing.T) { 29 testCases := []struct { 30 description string 31 topo *topology.CPUTopology 32 availableCPUs cpuset.CPUSet 33 expect []int 34 }{ 35 { 36 "single socket HT, 1 socket free", 37 topoSingleSocketHT, 38 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 39 []int{0}, 40 }, 41 { 42 "single socket HT, 0 sockets free", 43 topoSingleSocketHT, 44 cpuset.New(1, 2, 3, 4, 5, 6, 7), 45 []int{}, 46 }, 47 { 48 "dual socket HT, 2 sockets free", 49 topoDualSocketHT, 50 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 51 []int{0, 1}, 52 }, 53 { 54 "dual socket HT, 1 socket free", 55 topoDualSocketHT, 56 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11), 57 []int{1}, 58 }, 59 { 60 "dual socket HT, 0 sockets free", 61 topoDualSocketHT, 62 cpuset.New(0, 2, 3, 4, 5, 6, 7, 8, 9, 11), 63 []int{}, 64 }, 65 { 66 "dual socket, multi numa per socket, HT, 2 sockets free", 67 topoDualSocketMultiNumaPerSocketHT, 68 mustParseCPUSet(t, "0-79"), 69 []int{0, 1}, 70 }, 71 { 72 "dual socket, multi numa per socket, HT, 1 sockets free", 73 topoDualSocketMultiNumaPerSocketHT, 74 mustParseCPUSet(t, "1-79"), 75 []int{1}, 76 }, 77 { 78 "dual socket, multi numa per socket, HT, 0 sockets free", 79 topoDualSocketMultiNumaPerSocketHT, 80 mustParseCPUSet(t, "1-78"), 81 []int{}, 82 }, 83 { 84 "dual numa, multi socket per per socket, HT, 4 sockets free", 85 fakeTopoMultiSocketDualSocketPerNumaHT, 86 mustParseCPUSet(t, "0-79"), 87 []int{0, 1, 2, 3}, 88 }, 89 { 90 "dual numa, multi socket per per socket, HT, 3 sockets free", 91 fakeTopoMultiSocketDualSocketPerNumaHT, 92 mustParseCPUSet(t, "0-19,21-79"), 93 []int{0, 1, 3}, 94 }, 95 { 96 "dual numa, multi socket per per socket, HT, 2 sockets free", 97 fakeTopoMultiSocketDualSocketPerNumaHT, 98 mustParseCPUSet(t, "0-59,61-78"), 99 []int{0, 1}, 100 }, 101 { 102 "dual numa, multi socket per per socket, HT, 1 sockets free", 103 fakeTopoMultiSocketDualSocketPerNumaHT, 104 mustParseCPUSet(t, "1-19,21-38,41-60,61-78"), 105 []int{1}, 106 }, 107 { 108 "dual numa, multi socket per per socket, HT, 0 sockets free", 109 fakeTopoMultiSocketDualSocketPerNumaHT, 110 mustParseCPUSet(t, "0-40,42-49,51-68,71-79"), 111 []int{}, 112 }, 113 } 114 115 for _, tc := range testCases { 116 t.Run(tc.description, func(t *testing.T) { 117 acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0) 118 result := acc.freeSockets() 119 sort.Ints(result) 120 if !reflect.DeepEqual(result, tc.expect) { 121 t.Errorf("expected %v to equal %v", result, tc.expect) 122 123 } 124 }) 125 } 126 } 127 128 func TestCPUAccumulatorFreeNUMANodes(t *testing.T) { 129 testCases := []struct { 130 description string 131 topo *topology.CPUTopology 132 availableCPUs cpuset.CPUSet 133 expect []int 134 }{ 135 { 136 "single socket HT, 1 NUMA node free", 137 topoSingleSocketHT, 138 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 139 []int{0}, 140 }, 141 { 142 "single socket HT, 0 NUMA Node free", 143 topoSingleSocketHT, 144 cpuset.New(1, 2, 3, 4, 5, 6, 7), 145 []int{}, 146 }, 147 { 148 "dual socket HT, 2 NUMA Node free", 149 topoDualSocketHT, 150 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 151 []int{0, 1}, 152 }, 153 { 154 "dual socket HT, 1 NUMA Node free", 155 topoDualSocketHT, 156 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11), 157 []int{1}, 158 }, 159 { 160 "dual socket HT, 0 NUMA node free", 161 topoDualSocketHT, 162 cpuset.New(0, 2, 3, 4, 5, 6, 7, 8, 9, 11), 163 []int{}, 164 }, 165 { 166 "dual socket, multi numa per socket, HT, 4 NUMA Node free", 167 topoDualSocketMultiNumaPerSocketHT, 168 mustParseCPUSet(t, "0-79"), 169 []int{0, 1, 2, 3}, 170 }, 171 { 172 "dual socket, multi numa per socket, HT, 3 NUMA node free", 173 topoDualSocketMultiNumaPerSocketHT, 174 mustParseCPUSet(t, "1-79"), 175 []int{1, 2, 3}, 176 }, 177 { 178 "dual socket, multi numa per socket, HT, 2 NUMA node free", 179 topoDualSocketMultiNumaPerSocketHT, 180 mustParseCPUSet(t, "1-9,11-79"), 181 []int{2, 3}, 182 }, 183 { 184 "dual socket, multi numa per socket, HT, 1 NUMA node free", 185 topoDualSocketMultiNumaPerSocketHT, 186 mustParseCPUSet(t, "1-9,11-59,61-79"), 187 []int{3}, 188 }, 189 { 190 "dual socket, multi numa per socket, HT, 0 NUMA node free", 191 topoDualSocketMultiNumaPerSocketHT, 192 mustParseCPUSet(t, "1-9,11-59,61-78"), 193 []int{}, 194 }, 195 { 196 "dual numa, multi socket per per socket, HT, 2 NUMA node free", 197 fakeTopoMultiSocketDualSocketPerNumaHT, 198 mustParseCPUSet(t, "0-79"), 199 []int{0, 1}, 200 }, 201 { 202 "dual numa, multi socket per per socket, HT, 1 NUMA node free", 203 fakeTopoMultiSocketDualSocketPerNumaHT, 204 mustParseCPUSet(t, "0-9,11-79"), 205 []int{1}, 206 }, 207 { 208 "dual numa, multi socket per per socket, HT, 0 sockets free", 209 fakeTopoMultiSocketDualSocketPerNumaHT, 210 mustParseCPUSet(t, "0-9,11-59,61-79"), 211 []int{}, 212 }, 213 } 214 215 for _, tc := range testCases { 216 t.Run(tc.description, func(t *testing.T) { 217 acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0) 218 result := acc.freeNUMANodes() 219 if !reflect.DeepEqual(result, tc.expect) { 220 t.Errorf("expected %v to equal %v", result, tc.expect) 221 } 222 }) 223 } 224 } 225 226 func TestCPUAccumulatorFreeSocketsAndNUMANodes(t *testing.T) { 227 testCases := []struct { 228 description string 229 topo *topology.CPUTopology 230 availableCPUs cpuset.CPUSet 231 expectSockets []int 232 expectNUMANodes []int 233 }{ 234 { 235 "dual socket, multi numa per socket, HT, 2 Socket/4 NUMA Node free", 236 topoDualSocketMultiNumaPerSocketHT, 237 mustParseCPUSet(t, "0-79"), 238 []int{0, 1}, 239 []int{0, 1, 2, 3}, 240 }, 241 { 242 "dual socket, multi numa per socket, HT, 1 Socket/3 NUMA node free", 243 topoDualSocketMultiNumaPerSocketHT, 244 mustParseCPUSet(t, "1-79"), 245 []int{1}, 246 []int{1, 2, 3}, 247 }, 248 { 249 "dual socket, multi numa per socket, HT, 1 Socket/ 2 NUMA node free", 250 topoDualSocketMultiNumaPerSocketHT, 251 mustParseCPUSet(t, "1-9,11-79"), 252 []int{1}, 253 []int{2, 3}, 254 }, 255 { 256 "dual socket, multi numa per socket, HT, 0 Socket/ 2 NUMA node free", 257 topoDualSocketMultiNumaPerSocketHT, 258 mustParseCPUSet(t, "1-59,61-79"), 259 []int{}, 260 []int{1, 3}, 261 }, 262 } 263 264 for _, tc := range testCases { 265 t.Run(tc.description, func(t *testing.T) { 266 acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0) 267 resultNUMANodes := acc.freeNUMANodes() 268 if !reflect.DeepEqual(resultNUMANodes, tc.expectNUMANodes) { 269 t.Errorf("expected NUMA Nodes %v to equal %v", resultNUMANodes, tc.expectNUMANodes) 270 } 271 resultSockets := acc.freeSockets() 272 if !reflect.DeepEqual(resultSockets, tc.expectSockets) { 273 t.Errorf("expected Sockets %v to equal %v", resultSockets, tc.expectSockets) 274 } 275 }) 276 } 277 } 278 279 func TestCPUAccumulatorFreeCores(t *testing.T) { 280 testCases := []struct { 281 description string 282 topo *topology.CPUTopology 283 availableCPUs cpuset.CPUSet 284 expect []int 285 }{ 286 { 287 "single socket HT, 4 cores free", 288 topoSingleSocketHT, 289 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 290 []int{0, 1, 2, 3}, 291 }, 292 { 293 "single socket HT, 3 cores free", 294 topoSingleSocketHT, 295 cpuset.New(0, 1, 2, 4, 5, 6), 296 []int{0, 1, 2}, 297 }, 298 { 299 "single socket HT, 3 cores free (1 partially consumed)", 300 topoSingleSocketHT, 301 cpuset.New(0, 1, 2, 3, 4, 5, 6), 302 []int{0, 1, 2}, 303 }, 304 { 305 "single socket HT, 0 cores free", 306 topoSingleSocketHT, 307 cpuset.New(), 308 []int{}, 309 }, 310 { 311 "single socket HT, 0 cores free (4 partially consumed)", 312 topoSingleSocketHT, 313 cpuset.New(0, 1, 2, 3), 314 []int{}, 315 }, 316 { 317 "dual socket HT, 6 cores free", 318 topoDualSocketHT, 319 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 320 []int{0, 2, 4, 1, 3, 5}, 321 }, 322 { 323 "dual socket HT, 5 cores free (1 consumed from socket 0)", 324 topoDualSocketHT, 325 cpuset.New(2, 1, 3, 4, 5, 7, 8, 9, 10, 11), 326 []int{2, 4, 1, 3, 5}, 327 }, 328 { 329 "dual socket HT, 4 cores free (1 consumed from each socket)", 330 topoDualSocketHT, 331 cpuset.New(2, 3, 4, 5, 8, 9, 10, 11), 332 []int{2, 4, 3, 5}, 333 }, 334 } 335 336 for _, tc := range testCases { 337 t.Run(tc.description, func(t *testing.T) { 338 acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0) 339 result := acc.freeCores() 340 if !reflect.DeepEqual(result, tc.expect) { 341 t.Errorf("expected %v to equal %v", result, tc.expect) 342 } 343 }) 344 } 345 } 346 347 func TestCPUAccumulatorFreeCPUs(t *testing.T) { 348 testCases := []struct { 349 description string 350 topo *topology.CPUTopology 351 availableCPUs cpuset.CPUSet 352 expect []int 353 }{ 354 { 355 "single socket HT, 8 cpus free", 356 topoSingleSocketHT, 357 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 358 []int{0, 4, 1, 5, 2, 6, 3, 7}, 359 }, 360 { 361 "single socket HT, 5 cpus free", 362 topoSingleSocketHT, 363 cpuset.New(3, 4, 5, 6, 7), 364 []int{4, 5, 6, 3, 7}, 365 }, 366 { 367 "dual socket HT, 12 cpus free", 368 topoDualSocketHT, 369 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 370 []int{0, 6, 2, 8, 4, 10, 1, 7, 3, 9, 5, 11}, 371 }, 372 { 373 "dual socket HT, 11 cpus free", 374 topoDualSocketHT, 375 cpuset.New(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 376 []int{6, 2, 8, 4, 10, 1, 7, 3, 9, 5, 11}, 377 }, 378 { 379 "dual socket HT, 10 cpus free", 380 topoDualSocketHT, 381 cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11), 382 []int{2, 8, 4, 10, 1, 7, 3, 9, 5, 11}, 383 }, 384 { 385 "triple socket HT, 12 cpus free", 386 topoTripleSocketHT, 387 cpuset.New(0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 12, 13), 388 []int{12, 13, 0, 1, 2, 3, 6, 7, 8, 9, 10, 11}, 389 }, 390 } 391 392 for _, tc := range testCases { 393 t.Run(tc.description, func(t *testing.T) { 394 acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0) 395 result := acc.freeCPUs() 396 if !reflect.DeepEqual(result, tc.expect) { 397 t.Errorf("expected %v to equal %v", result, tc.expect) 398 } 399 }) 400 } 401 } 402 403 func TestCPUAccumulatorTake(t *testing.T) { 404 testCases := []struct { 405 description string 406 topo *topology.CPUTopology 407 availableCPUs cpuset.CPUSet 408 takeCPUs []cpuset.CPUSet 409 numCPUs int 410 expectSatisfied bool 411 expectFailed bool 412 }{ 413 { 414 "take 0 cpus from a single socket HT, require 1", 415 topoSingleSocketHT, 416 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 417 []cpuset.CPUSet{cpuset.New()}, 418 1, 419 false, 420 false, 421 }, 422 { 423 "take 0 cpus from a single socket HT, require 1, none available", 424 topoSingleSocketHT, 425 cpuset.New(), 426 []cpuset.CPUSet{cpuset.New()}, 427 1, 428 false, 429 true, 430 }, 431 { 432 "take 1 cpu from a single socket HT, require 1", 433 topoSingleSocketHT, 434 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 435 []cpuset.CPUSet{cpuset.New(0)}, 436 1, 437 true, 438 false, 439 }, 440 { 441 "take 1 cpu from a single socket HT, require 2", 442 topoSingleSocketHT, 443 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 444 []cpuset.CPUSet{cpuset.New(0)}, 445 2, 446 false, 447 false, 448 }, 449 { 450 "take 2 cpu from a single socket HT, require 4, expect failed", 451 topoSingleSocketHT, 452 cpuset.New(0, 1, 2), 453 []cpuset.CPUSet{cpuset.New(0), cpuset.New(1)}, 454 4, 455 false, 456 true, 457 }, 458 { 459 "take all cpus one at a time from a single socket HT, require 8", 460 topoSingleSocketHT, 461 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 462 []cpuset.CPUSet{ 463 cpuset.New(0), 464 cpuset.New(1), 465 cpuset.New(2), 466 cpuset.New(3), 467 cpuset.New(4), 468 cpuset.New(5), 469 cpuset.New(6), 470 cpuset.New(7), 471 }, 472 8, 473 true, 474 false, 475 }, 476 } 477 478 for _, tc := range testCases { 479 t.Run(tc.description, func(t *testing.T) { 480 acc := newCPUAccumulator(tc.topo, tc.availableCPUs, tc.numCPUs) 481 totalTaken := 0 482 for _, cpus := range tc.takeCPUs { 483 acc.take(cpus) 484 totalTaken += cpus.Size() 485 } 486 if tc.expectSatisfied != acc.isSatisfied() { 487 t.Errorf("expected acc.isSatisfied() to be %t", tc.expectSatisfied) 488 } 489 if tc.expectFailed != acc.isFailed() { 490 t.Errorf("expected acc.isFailed() to be %t", tc.expectFailed) 491 } 492 for _, cpus := range tc.takeCPUs { 493 availableCPUs := acc.details.CPUs() 494 if cpus.Intersection(availableCPUs).Size() > 0 { 495 t.Errorf("expected intersection of taken cpus [%s] and acc.details.CPUs() [%s] to be empty", cpus, availableCPUs) 496 } 497 if !cpus.IsSubsetOf(acc.result) { 498 t.Errorf("expected [%s] to be a subset of acc.result [%s]", cpus, acc.result) 499 } 500 } 501 expNumCPUsNeeded := tc.numCPUs - totalTaken 502 if acc.numCPUsNeeded != expNumCPUsNeeded { 503 t.Errorf("expected acc.numCPUsNeeded to be %d (got %d)", expNumCPUsNeeded, acc.numCPUsNeeded) 504 } 505 }) 506 } 507 } 508 509 type takeByTopologyTestCase struct { 510 description string 511 topo *topology.CPUTopology 512 availableCPUs cpuset.CPUSet 513 numCPUs int 514 expErr string 515 expResult cpuset.CPUSet 516 } 517 518 func commonTakeByTopologyTestCases(t *testing.T) []takeByTopologyTestCase { 519 return []takeByTopologyTestCase{ 520 { 521 "take more cpus than are available from single socket with HT", 522 topoSingleSocketHT, 523 cpuset.New(0, 2, 4, 6), 524 5, 525 "not enough cpus available to satisfy request: requested=5, available=4", 526 cpuset.New(), 527 }, 528 { 529 "take zero cpus from single socket with HT", 530 topoSingleSocketHT, 531 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 532 0, 533 "", 534 cpuset.New(), 535 }, 536 { 537 "take one cpu from single socket with HT", 538 topoSingleSocketHT, 539 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 540 1, 541 "", 542 cpuset.New(0), 543 }, 544 { 545 "take one cpu from single socket with HT, some cpus are taken", 546 topoSingleSocketHT, 547 cpuset.New(1, 3, 5, 6, 7), 548 1, 549 "", 550 cpuset.New(6), 551 }, 552 { 553 "take two cpus from single socket with HT", 554 topoSingleSocketHT, 555 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 556 2, 557 "", 558 cpuset.New(0, 4), 559 }, 560 { 561 "take all cpus from single socket with HT", 562 topoSingleSocketHT, 563 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 564 8, 565 "", 566 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 567 }, 568 { 569 "take two cpus from single socket with HT, only one core totally free", 570 topoSingleSocketHT, 571 cpuset.New(0, 1, 2, 3, 6), 572 2, 573 "", 574 cpuset.New(2, 6), 575 }, 576 { 577 "take a socket of cpus from dual socket with HT", 578 topoDualSocketHT, 579 cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 580 6, 581 "", 582 cpuset.New(0, 2, 4, 6, 8, 10), 583 }, 584 { 585 "take a socket of cpus from dual socket with multi-numa-per-socket with HT", 586 topoDualSocketMultiNumaPerSocketHT, 587 mustParseCPUSet(t, "0-79"), 588 40, 589 "", 590 mustParseCPUSet(t, "0-19,40-59"), 591 }, 592 { 593 "take a NUMA node of cpus from dual socket with multi-numa-per-socket with HT", 594 topoDualSocketMultiNumaPerSocketHT, 595 mustParseCPUSet(t, "0-79"), 596 20, 597 "", 598 mustParseCPUSet(t, "0-9,40-49"), 599 }, 600 { 601 "take a NUMA node of cpus from dual socket with multi-numa-per-socket with HT, with 1 NUMA node already taken", 602 topoDualSocketMultiNumaPerSocketHT, 603 mustParseCPUSet(t, "10-39,50-79"), 604 20, 605 "", 606 mustParseCPUSet(t, "10-19,50-59"), 607 }, 608 { 609 "take a socket and a NUMA node of cpus from dual socket with multi-numa-per-socket with HT", 610 topoDualSocketMultiNumaPerSocketHT, 611 mustParseCPUSet(t, "0-79"), 612 60, 613 "", 614 mustParseCPUSet(t, "0-29,40-69"), 615 }, 616 { 617 "take a socket and a NUMA node of cpus from dual socket with multi-numa-per-socket with HT, a core taken", 618 topoDualSocketMultiNumaPerSocketHT, 619 mustParseCPUSet(t, "1-39,41-79"), // reserve the first (phys) core (0,40) 620 60, 621 "", 622 mustParseCPUSet(t, "10-39,50-79"), 623 }, 624 } 625 } 626 627 func TestTakeByTopologyNUMAPacked(t *testing.T) { 628 testCases := commonTakeByTopologyTestCases(t) 629 testCases = append(testCases, []takeByTopologyTestCase{ 630 { 631 "take one cpu from dual socket with HT - core from Socket 0", 632 topoDualSocketHT, 633 cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11), 634 1, 635 "", 636 cpuset.New(2), 637 }, 638 { 639 "allocate 4 full cores with 3 coming from the first NUMA node (filling it up) and 1 coming from the second NUMA node", 640 topoDualSocketHT, 641 mustParseCPUSet(t, "0-11"), 642 8, 643 "", 644 mustParseCPUSet(t, "0,6,2,8,4,10,1,7"), 645 }, 646 { 647 "allocate 32 full cores with 30 coming from the first 3 NUMA nodes (filling them up) and 2 coming from the fourth NUMA node", 648 topoDualSocketMultiNumaPerSocketHT, 649 mustParseCPUSet(t, "0-79"), 650 64, 651 "", 652 mustParseCPUSet(t, "0-29,40-69,30,31,70,71"), 653 }, 654 }...) 655 656 for _, tc := range testCases { 657 t.Run(tc.description, func(t *testing.T) { 658 result, err := takeByTopologyNUMAPacked(tc.topo, tc.availableCPUs, tc.numCPUs) 659 if tc.expErr != "" && err != nil && err.Error() != tc.expErr { 660 t.Errorf("expected error to be [%v] but it was [%v]", tc.expErr, err) 661 } 662 if !result.Equals(tc.expResult) { 663 t.Errorf("expected result [%s] to equal [%s]", result, tc.expResult) 664 } 665 }) 666 } 667 } 668 669 type takeByTopologyExtendedTestCase struct { 670 description string 671 topo *topology.CPUTopology 672 availableCPUs cpuset.CPUSet 673 numCPUs int 674 cpuGroupSize int 675 expErr string 676 expResult cpuset.CPUSet 677 } 678 679 func commonTakeByTopologyExtendedTestCases(t *testing.T) []takeByTopologyExtendedTestCase { 680 var extendedTestCases []takeByTopologyExtendedTestCase 681 682 testCases := commonTakeByTopologyTestCases(t) 683 for _, tc := range testCases { 684 extendedTestCases = append(extendedTestCases, takeByTopologyExtendedTestCase{ 685 tc.description, 686 tc.topo, 687 tc.availableCPUs, 688 tc.numCPUs, 689 1, 690 tc.expErr, 691 tc.expResult, 692 }) 693 } 694 695 extendedTestCases = append(extendedTestCases, []takeByTopologyExtendedTestCase{ 696 { 697 "allocate 4 full cores with 2 distributed across each NUMA node", 698 topoDualSocketHT, 699 mustParseCPUSet(t, "0-11"), 700 8, 701 1, 702 "", 703 mustParseCPUSet(t, "0,6,2,8,1,7,3,9"), 704 }, 705 { 706 "allocate 32 full cores with 8 distributed across each NUMA node", 707 topoDualSocketMultiNumaPerSocketHT, 708 mustParseCPUSet(t, "0-79"), 709 64, 710 1, 711 "", 712 mustParseCPUSet(t, "0-7,10-17,20-27,30-37,40-47,50-57,60-67,70-77"), 713 }, 714 { 715 "allocate 24 full cores with 8 distributed across the first 3 NUMA nodes", 716 topoDualSocketMultiNumaPerSocketHT, 717 mustParseCPUSet(t, "0-79"), 718 48, 719 1, 720 "", 721 mustParseCPUSet(t, "0-7,10-17,20-27,40-47,50-57,60-67"), 722 }, 723 { 724 "allocate 24 full cores with 8 distributed across the first 3 NUMA nodes (taking all but 2 from the first NUMA node)", 725 topoDualSocketMultiNumaPerSocketHT, 726 mustParseCPUSet(t, "1-29,32-39,41-69,72-79"), 727 48, 728 1, 729 "", 730 mustParseCPUSet(t, "1-8,10-17,20-27,41-48,50-57,60-67"), 731 }, 732 { 733 "allocate 24 full cores with 8 distributed across the last 3 NUMA nodes (even though all 8 could be allocated from the first NUMA node)", 734 topoDualSocketMultiNumaPerSocketHT, 735 mustParseCPUSet(t, "2-29,31-39,42-69,71-79"), 736 48, 737 1, 738 "", 739 mustParseCPUSet(t, "10-17,20-27,31-38,50-57,60-67,71-78"), 740 }, 741 { 742 "allocate 8 full cores with 2 distributed across each NUMA node", 743 topoDualSocketMultiNumaPerSocketHT, 744 mustParseCPUSet(t, "0-2,10-12,20-22,30-32,40-41,50-51,60-61,70-71"), 745 16, 746 1, 747 "", 748 mustParseCPUSet(t, "0-1,10-11,20-21,30-31,40-41,50-51,60-61,70-71"), 749 }, 750 { 751 "allocate 8 full cores with 2 distributed across each NUMA node", 752 topoDualSocketMultiNumaPerSocketHT, 753 mustParseCPUSet(t, "0-2,10-12,20-22,30-32,40-41,50-51,60-61,70-71"), 754 16, 755 1, 756 "", 757 mustParseCPUSet(t, "0-1,10-11,20-21,30-31,40-41,50-51,60-61,70-71"), 758 }, 759 }...) 760 761 return extendedTestCases 762 } 763 764 func TestTakeByTopologyNUMADistributed(t *testing.T) { 765 testCases := commonTakeByTopologyExtendedTestCases(t) 766 testCases = append(testCases, []takeByTopologyExtendedTestCase{ 767 { 768 "take one cpu from dual socket with HT - core from Socket 0", 769 topoDualSocketHT, 770 cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11), 771 1, 772 1, 773 "", 774 cpuset.New(1), 775 }, 776 { 777 "take one cpu from dual socket with HT - core from Socket 0 - cpuGroupSize 2", 778 topoDualSocketHT, 779 cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11), 780 1, 781 2, 782 "", 783 cpuset.New(2), 784 }, 785 { 786 "allocate 13 full cores distributed across the first 2 NUMA nodes", 787 topoDualSocketMultiNumaPerSocketHT, 788 mustParseCPUSet(t, "0-79"), 789 26, 790 1, 791 "", 792 mustParseCPUSet(t, "0-6,10-16,40-45,50-55"), 793 }, 794 { 795 "allocate 13 full cores distributed across the first 2 NUMA nodes (cpuGroupSize 2)", 796 topoDualSocketMultiNumaPerSocketHT, 797 mustParseCPUSet(t, "0-79"), 798 26, 799 2, 800 "", 801 mustParseCPUSet(t, "0-6,10-15,40-46,50-55"), 802 }, 803 { 804 "allocate 31 full cores with 15 CPUs distributed across each NUMA node and 1 CPU spilling over to each of NUMA 0, 1", 805 topoDualSocketMultiNumaPerSocketHT, 806 mustParseCPUSet(t, "0-79"), 807 62, 808 1, 809 "", 810 mustParseCPUSet(t, "0-7,10-17,20-27,30-37,40-47,50-57,60-66,70-76"), 811 }, 812 { 813 "allocate 31 full cores with 14 CPUs distributed across each NUMA node and 2 CPUs spilling over to each of NUMA 0, 1, 2 (cpuGroupSize 2)", 814 topoDualSocketMultiNumaPerSocketHT, 815 mustParseCPUSet(t, "0-79"), 816 62, 817 2, 818 "", 819 mustParseCPUSet(t, "0-7,10-17,20-27,30-36,40-47,50-57,60-67,70-76"), 820 }, 821 { 822 "allocate 31 full cores with 15 CPUs distributed across each NUMA node and 1 CPU spilling over to each of NUMA 2, 3 (to keep balance)", 823 topoDualSocketMultiNumaPerSocketHT, 824 mustParseCPUSet(t, "0-8,10-18,20-39,40-48,50-58,60-79"), 825 62, 826 1, 827 "", 828 mustParseCPUSet(t, "0-7,10-17,20-27,30-37,40-46,50-56,60-67,70-77"), 829 }, 830 { 831 "allocate 31 full cores with 14 CPUs distributed across each NUMA node and 2 CPUs spilling over to each of NUMA 0, 2, 3 (to keep balance with cpuGroupSize 2)", 832 topoDualSocketMultiNumaPerSocketHT, 833 mustParseCPUSet(t, "0-8,10-18,20-39,40-48,50-58,60-79"), 834 62, 835 2, 836 "", 837 mustParseCPUSet(t, "0-7,10-16,20-27,30-37,40-47,50-56,60-67,70-77"), 838 }, 839 { 840 "ensure bestRemainder chosen with NUMA nodes that have enough CPUs to satisfy the request", 841 topoDualSocketMultiNumaPerSocketHT, 842 mustParseCPUSet(t, "0-3,10-13,20-23,30-36,40-43,50-53,60-63,70-76"), 843 34, 844 1, 845 "", 846 mustParseCPUSet(t, "0-3,10-13,20-23,30-34,40-43,50-53,60-63,70-74"), 847 }, 848 { 849 "ensure previous failure encountered on live machine has been fixed (1/1)", 850 topoDualSocketMultiNumaPerSocketHTLarge, 851 mustParseCPUSet(t, "0,128,30,31,158,159,43-47,171-175,62,63,190,191,75-79,203-207,94,96,222,223,101-111,229-239,126,127,254,255"), 852 28, 853 1, 854 "", 855 mustParseCPUSet(t, "43-47,75-79,96,101-105,171-174,203-206,229-232"), 856 }, 857 }...) 858 859 for _, tc := range testCases { 860 t.Run(tc.description, func(t *testing.T) { 861 result, err := takeByTopologyNUMADistributed(tc.topo, tc.availableCPUs, tc.numCPUs, tc.cpuGroupSize) 862 if err != nil { 863 if tc.expErr == "" { 864 t.Errorf("unexpected error [%v]", err) 865 } 866 if tc.expErr != "" && err.Error() != tc.expErr { 867 t.Errorf("expected error to be [%v] but it was [%v]", tc.expErr, err) 868 } 869 return 870 } 871 if !result.Equals(tc.expResult) { 872 t.Errorf("expected result [%s] to equal [%s]", result, tc.expResult) 873 } 874 }) 875 } 876 } 877 878 func mustParseCPUSet(t *testing.T, s string) cpuset.CPUSet { 879 cpus, err := cpuset.Parse(s) 880 if err != nil { 881 t.Errorf("parsing %q: %v", s, err) 882 } 883 return cpus 884 }