|
9 | 9 | #include <gtest/gtest.h> |
10 | 10 |
|
11 | 11 | #include "comms/ctran/commstate/CommStateX.h" |
| 12 | +#include "comms/ctran/tests/VerifyCommStateXUtil.h" |
12 | 13 | #include "comms/testinfra/TestXPlatUtils.h" |
13 | 14 |
|
14 | 15 | namespace ncclx { |
@@ -552,49 +553,114 @@ TEST(CommStateXTest, CommRankToWorldRanks) { |
552 | 553 | EXPECT_EQ(commState->gRank(3), 7); |
553 | 554 | } |
554 | 555 |
|
555 | | -TEST(CommStateXTest, gPidTest) { |
556 | | - const int nRanks = 4; |
557 | | - const int cudaDev = 0; |
558 | | - const int cudaArch = 90; |
559 | | - const int64_t busId = 25; |
560 | | - const uint64_t commHash = 0; |
561 | | - const std::string kSu; |
| 556 | +// Parameterized test: verify host()/gPid() preserve real values and node |
| 557 | +// grouping is correct across all virtual topology modes. |
| 558 | +enum class TopoMode { kSystem, kNolocal, kVnode, kVClique }; |
562 | 559 |
|
563 | | - std::vector<RankTopology> rankTopologies{}; |
564 | | - rankTopologies.emplace_back( |
565 | | - createRankTopology(0, kDc, kZone, kSu, kRtsw0, kHost0, -1, 1000)); |
566 | | - rankTopologies.emplace_back( |
567 | | - createRankTopology(1, kDc, kZone, kSu, kRtsw0, kHost0, -1, 1001)); |
568 | | - rankTopologies.emplace_back( |
569 | | - createRankTopology(2, kDc, kZone, kSu, kRtsw0, kHost1, -1, 2000)); |
570 | | - rankTopologies.emplace_back( |
571 | | - createRankTopology(3, kDc, kZone, kSu, kRtsw0, kHost1, -1, 2001)); |
| 560 | +struct TopoTestParam { |
| 561 | + TopoMode mode; |
| 562 | + int vCliqueSize; // only used for kVClique |
| 563 | + int expectedNNodes; |
| 564 | + int expectedNLocalRanks; // for rank 0 |
| 565 | +}; |
572 | 566 |
|
573 | | - for (int rank = 0; rank < nRanks; ++rank) { |
574 | | - auto commState = std::make_unique<CommStateX>( |
575 | | - rank, |
576 | | - nRanks, |
577 | | - cudaDev, |
578 | | - cudaArch, |
579 | | - busId, |
580 | | - commHash, |
581 | | - rankTopologies, |
582 | | - std::vector<int>{}); |
583 | | - |
584 | | - // Test gPid() for default (current) rank |
585 | | - std::string expectedGPid = std::string(rankTopologies[rank].host) + ":" + |
586 | | - std::to_string(rankTopologies[rank].pid); |
587 | | - EXPECT_EQ(commState->gPid(), expectedGPid); |
588 | | - |
589 | | - // Test gPid(rank) for all ranks |
590 | | - for (int r = 0; r < nRanks; ++r) { |
591 | | - std::string expected = std::string(rankTopologies[r].host) + ":" + |
592 | | - std::to_string(rankTopologies[r].pid); |
593 | | - EXPECT_EQ(commState->gPid(r), expected); |
| 567 | +std::string topoTestName(const testing::TestParamInfo<TopoTestParam>& info) { |
| 568 | + switch (info.param.mode) { |
| 569 | + case TopoMode::kSystem: |
| 570 | + return "system"; |
| 571 | + case TopoMode::kNolocal: |
| 572 | + return "nolocal"; |
| 573 | + case TopoMode::kVnode: |
| 574 | + return "vnode4"; |
| 575 | + case TopoMode::kVClique: |
| 576 | + return "vclique" + std::to_string(info.param.vCliqueSize); |
| 577 | + } |
| 578 | + return "unknown"; |
| 579 | +} |
| 580 | + |
| 581 | +class GpidTopoTest : public ::testing::TestWithParam<TopoTestParam> { |
| 582 | + protected: |
| 583 | + static constexpr int kRanksPerHost = 4; |
| 584 | + static constexpr int kNumHosts = 2; |
| 585 | + static constexpr int kNRanks = kRanksPerHost * kNumHosts; |
| 586 | + static constexpr int kCudaDev = 0; |
| 587 | + static constexpr int kCudaArch = 90; |
| 588 | + static constexpr int64_t kBusId = 25; |
| 589 | + static constexpr uint64_t kCommHash = 0; |
| 590 | + |
| 591 | + std::vector<RankTopology> makeRankTopologies() { |
| 592 | + const std::string kSu; |
| 593 | + const std::array<std::pair<const char*, const char*>, kNumHosts> hostInfo = |
| 594 | + {{{kHost0, kRtsw0}, {kHost1, kRtsw1}}}; |
| 595 | + std::vector<RankTopology> topos; |
| 596 | + topos.reserve(kNRanks); |
| 597 | + for (int r = 0; r < kNRanks; r++) { |
| 598 | + const auto& [host, rtsw] = hostInfo[r / kRanksPerHost]; |
| 599 | + const int pid = 1000 * (r / kRanksPerHost + 1) + r % kRanksPerHost; |
| 600 | + topos.emplace_back( |
| 601 | + createRankTopology(r, kDc, kZone, kSu, rtsw, host, -1, pid)); |
594 | 602 | } |
| 603 | + return topos; |
| 604 | + } |
| 605 | +}; |
| 606 | + |
| 607 | +TEST_P(GpidTopoTest, HostAndGpidPreserved) { |
| 608 | + const auto& [mode, vCliqueSize, expectedNNodes, expectedNLocalRanks] = |
| 609 | + GetParam(); |
| 610 | + auto rankTopologies = makeRankTopologies(); |
| 611 | + |
| 612 | + // Set CVAR for vnode mode (needs ncclx EnvRAII, not SysEnvRAII) |
| 613 | + // nolocal and vClique are passed via constructor params instead. |
| 614 | + std::unique_ptr<SysEnvRAII> envTopo, envPpn; |
| 615 | + if (mode == TopoMode::kVnode) { |
| 616 | + envTopo = |
| 617 | + std::make_unique<SysEnvRAII>("NCCL_COMM_STATE_DEBUG_TOPO", "vnode"); |
| 618 | + envPpn = std::make_unique<SysEnvRAII>( |
| 619 | + "NCCL_COMM_STATE_DEBUG_TOPO_VNODE_NLOCALRANKS", "4"); |
595 | 620 | } |
| 621 | + |
| 622 | + const bool noLocal = (mode == TopoMode::kNolocal); |
| 623 | + auto commState = std::make_unique<CommStateX>( |
| 624 | + 0, |
| 625 | + kNRanks, |
| 626 | + kCudaDev, |
| 627 | + kCudaArch, |
| 628 | + kBusId, |
| 629 | + kCommHash, |
| 630 | + rankTopologies, |
| 631 | + std::vector<int>{}, |
| 632 | + "" /* commDesc */, |
| 633 | + noLocal, |
| 634 | + vCliqueSize); |
| 635 | + |
| 636 | + // Node grouping matches expected virtual topology |
| 637 | + EXPECT_EQ(commState->nNodes(), expectedNNodes); |
| 638 | + EXPECT_EQ(commState->nLocalRanks(0), expectedNLocalRanks); |
| 639 | + |
| 640 | + // Real hostname and gPid preserved for all ranks |
| 641 | + using Helper = ctran::testing::VerifyCommStateXHelper; |
| 642 | + for (int r = 0; r < kNRanks; ++r) { |
| 643 | + Helper::verifyHost(commState.get(), r, rankTopologies[r].host); |
| 644 | + Helper::verifyGPid( |
| 645 | + commState.get(), r, rankTopologies[r].host, rankTopologies[r].pid); |
| 646 | + } |
| 647 | + Helper::verifyGPidUniqueness(commState.get(), kNRanks); |
596 | 648 | } |
597 | 649 |
|
| 650 | +INSTANTIATE_TEST_SUITE_P( |
| 651 | + CommStateXTest, |
| 652 | + GpidTopoTest, |
| 653 | + ::testing::Values( |
| 654 | + // system: 2 real hosts × 4 ranks each |
| 655 | + TopoTestParam{TopoMode::kSystem, 0, 2, 4}, |
| 656 | + // nolocal: each rank is its own node |
| 657 | + TopoTestParam{TopoMode::kNolocal, 0, 8, 1}, |
| 658 | + // vnode with nLocalRanks=4: 2 virtual nodes |
| 659 | + TopoTestParam{TopoMode::kVnode, 0, 2, 4}, |
| 660 | + // vClique size=2: 4 virtual nodes of 2 ranks each |
| 661 | + TopoTestParam{TopoMode::kVClique, 2, 4, 2}), |
| 662 | + topoTestName); |
| 663 | + |
598 | 664 | TEST(CommStateXTest, TopologySetInvalidNvlFabricTopos) { |
599 | 665 | const int rank = 0; |
600 | 666 | const int nRanks = 4; |
@@ -663,9 +729,7 @@ TEST(CommStateXTest, nvlFabricWithNoLocal) { |
663 | 729 | "" /* commDesc */, |
664 | 730 | true /* noLocal */); |
665 | 731 |
|
666 | | - // noLocal is set at construction; initRankStatesTopology delegates to |
667 | | - // initRankTopologyNolocal |
668 | | - commState->initRankStatesTopology(nullptr); |
| 732 | + commState->initRankTopologyNolocal(); |
669 | 733 |
|
670 | 734 | // Set up NVL fabric with 2 clusters of 4 ranks each (e.g. GB200 2-GPU trays) |
671 | 735 | std::vector<NvlFabricTopology> nvlFabricTopologies{}; |
|
0 commit comments