|
6 | 6 | #include <folly/logging/xlog.h> |
7 | 7 | #include <array> |
8 | 8 |
|
9 | | -#include <chrono> |
10 | 9 | #include <memory> |
11 | 10 | #include <string> |
| 11 | +#include <tuple> |
12 | 12 | #include <vector> |
13 | 13 |
|
14 | 14 | #ifdef __HIP_PLATFORM_AMD__ |
@@ -1791,6 +1791,179 @@ TEST_P( |
1791 | 1791 | } |
1792 | 1792 | } |
1793 | 1793 |
|
| 1794 | +TEST_F( |
| 1795 | + MultipeerIbgdaTransportTestFixture, |
| 1796 | + RegisteredSendTailsDrainAndStagingIsolation) { |
| 1797 | + if (numRanks != 2) { |
| 1798 | + GTEST_SKIP() << "Skipping test: requires exactly 2 ranks, got " << numRanks; |
| 1799 | + } |
| 1800 | + if (!test::supportsProgressSendRecv()) { |
| 1801 | + GTEST_SKIP() << "registered-source send is not supported for this build"; |
| 1802 | + } |
| 1803 | + |
| 1804 | + constexpr std::size_t perChannelSize = 64 * 1024; |
| 1805 | + constexpr int pipelineDepth = 2; |
| 1806 | + constexpr std::size_t pipelineChunk = perChannelSize / pipelineDepth; |
| 1807 | + constexpr std::size_t maxSignalBytes = 4 * 1024; |
| 1808 | + constexpr std::size_t simpleProtocolDataBytes = 16; |
| 1809 | + constexpr int numBlocks = 1; |
| 1810 | + constexpr int blockSize = 128; |
| 1811 | + constexpr uint8_t stagingPoison = 0xA5; |
| 1812 | + constexpr std::size_t zeroByteAfterPostedIndex = 2; |
| 1813 | + const int peerRank = globalRank == 0 ? 1 : 0; |
| 1814 | + const std::array<std::size_t, 8> sizes{ |
| 1815 | + 0, |
| 1816 | + 1, |
| 1817 | + 7, |
| 1818 | + simpleProtocolDataBytes - 1, |
| 1819 | + simpleProtocolDataBytes, |
| 1820 | + simpleProtocolDataBytes + 1, |
| 1821 | + pipelineChunk - 1, |
| 1822 | + pipelineChunk + 1, |
| 1823 | + }; |
| 1824 | + |
| 1825 | + std::unique_ptr<MultipeerIbgdaTransport> transport; |
| 1826 | + try { |
| 1827 | + MultipeerIbgdaTransportConfig config{ |
| 1828 | + .cudaDevice = localRank, |
| 1829 | + .perChannelSize = perChannelSize, |
| 1830 | + .max_num_channels = numBlocks, |
| 1831 | + .pipelineDepth = pipelineDepth, |
| 1832 | + }; |
| 1833 | + auto bootstrap = std::make_shared<meta::comms::MpiBootstrap>(); |
| 1834 | + transport = std::make_unique<MultipeerIbgdaTransport>( |
| 1835 | + globalRank, numRanks, bootstrap, config); |
| 1836 | + transport->exchange(); |
| 1837 | + } catch (const std::exception& e) { |
| 1838 | + GTEST_SKIP() << "IBGDA transport not available: " << e.what(); |
| 1839 | + } |
| 1840 | + |
| 1841 | + auto* peerTransport = transport->getP2pTransportDevice(peerRank); |
| 1842 | + DeviceBuffer errorCountBuffer(sizeof(int)); |
| 1843 | + DeviceBuffer observationBuffer(sizeof(test::RegisteredSendObservation)); |
| 1844 | + auto* errorCount = static_cast<int*>(errorCountBuffer.get()); |
| 1845 | + auto* observation = |
| 1846 | + static_cast<test::RegisteredSendObservation*>(observationBuffer.get()); |
| 1847 | + |
| 1848 | + test::testFillTransportStaging( |
| 1849 | + peerTransport, |
| 1850 | + globalRank == 0, |
| 1851 | + 0, |
| 1852 | + perChannelSize, |
| 1853 | + stagingPoison, |
| 1854 | + numBlocks, |
| 1855 | + blockSize); |
| 1856 | + CUDACHECK_TEST(cudaDeviceSynchronize()); |
| 1857 | + |
| 1858 | + for (std::size_t index = 0; index < sizes.size(); ++index) { |
| 1859 | + const std::size_t nbytes = sizes[index]; |
| 1860 | + const std::size_t allocationBytes = nbytes == 0 ? 1 : nbytes; |
| 1861 | + const uint8_t pattern = static_cast<uint8_t>(0x20 + index * 13); |
| 1862 | + DeviceBuffer sendBuffer(allocationBytes); |
| 1863 | + DeviceBuffer recvBuffer(allocationBytes); |
| 1864 | + IbgdaLocalBuffer registeredSource{}; |
| 1865 | + if (globalRank == 0) { |
| 1866 | + if (nbytes > 0) { |
| 1867 | + registeredSource = transport->registerBuffer(sendBuffer.get(), nbytes); |
| 1868 | + } |
| 1869 | + test::fillBufferWithPattern( |
| 1870 | + sendBuffer.get(), nbytes, pattern, numBlocks, blockSize); |
| 1871 | + CUDACHECK_TEST( |
| 1872 | + cudaMemset(observation, 0, sizeof(test::RegisteredSendObservation))); |
| 1873 | + } else { |
| 1874 | + CUDACHECK_TEST(cudaMemset(recvBuffer.get(), 0, allocationBytes)); |
| 1875 | + } |
| 1876 | + CUDACHECK_TEST(cudaDeviceSynchronize()); |
| 1877 | + MPI_CHECK(MPI_Barrier(MPI_COMM_WORLD)); |
| 1878 | + |
| 1879 | + test::testRegisteredSendRecv( |
| 1880 | + peerTransport, |
| 1881 | + registeredSource, |
| 1882 | + recvBuffer.get(), |
| 1883 | + nbytes, |
| 1884 | + maxSignalBytes, |
| 1885 | + globalRank == 0, |
| 1886 | + numBlocks, |
| 1887 | + blockSize, |
| 1888 | + globalRank == 0 ? observation : nullptr, |
| 1889 | + index == 4, |
| 1890 | + globalRank == 0 && nbytes > 0, |
| 1891 | + static_cast<uint8_t>(pattern ^ 0xFF), |
| 1892 | + index == zeroByteAfterPostedIndex); |
| 1893 | + CUDACHECK_TEST(cudaDeviceSynchronize()); |
| 1894 | + MPI_CHECK(MPI_Barrier(MPI_COMM_WORLD)); |
| 1895 | + |
| 1896 | + if (globalRank == 0) { |
| 1897 | + test::RegisteredSendObservation hostObservation{}; |
| 1898 | + CUDACHECK_TEST(cudaMemcpy( |
| 1899 | + &hostObservation, |
| 1900 | + observation, |
| 1901 | + sizeof(hostObservation), |
| 1902 | + cudaMemcpyDeviceToHost)); |
| 1903 | + if (index == 4) { |
| 1904 | + EXPECT_EQ(hostObservation.postedCount, 0); |
| 1905 | + } else if (index == zeroByteAfterPostedIndex) { |
| 1906 | + EXPECT_EQ(hostObservation.postedCount, 2); |
| 1907 | + } else { |
| 1908 | + EXPECT_EQ(hostObservation.postedCount, 1); |
| 1909 | + } |
| 1910 | + EXPECT_EQ(hostObservation.drainedCount, 1); |
| 1911 | + } else { |
| 1912 | + CUDACHECK_TEST(cudaMemset(errorCount, 0, sizeof(int))); |
| 1913 | + test::verifyBufferPattern( |
| 1914 | + recvBuffer.get(), nbytes, pattern, errorCount, numBlocks, blockSize); |
| 1915 | + CUDACHECK_TEST(cudaDeviceSynchronize()); |
| 1916 | + int hostErrors = 0; |
| 1917 | + CUDACHECK_TEST(cudaMemcpy( |
| 1918 | + &hostErrors, errorCount, sizeof(hostErrors), cudaMemcpyDeviceToHost)); |
| 1919 | + EXPECT_EQ(hostErrors, 0) << "registered send corrupted size " << nbytes; |
| 1920 | + } |
| 1921 | + |
| 1922 | + if (index == 1 && globalRank == 1) { |
| 1923 | + CUDACHECK_TEST(cudaMemset(errorCount, 0, sizeof(int))); |
| 1924 | + test::testVerifyTransportStaging( |
| 1925 | + peerTransport, |
| 1926 | + false, |
| 1927 | + 1, |
| 1928 | + simpleProtocolDataBytes - 1, |
| 1929 | + stagingPoison, |
| 1930 | + errorCount, |
| 1931 | + numBlocks, |
| 1932 | + blockSize); |
| 1933 | + CUDACHECK_TEST(cudaDeviceSynchronize()); |
| 1934 | + int hostErrors = 0; |
| 1935 | + CUDACHECK_TEST(cudaMemcpy( |
| 1936 | + &hostErrors, errorCount, sizeof(hostErrors), cudaMemcpyDeviceToHost)); |
| 1937 | + EXPECT_EQ(hostErrors, 0) |
| 1938 | + << "registered send wrote rounded tail bytes into recv staging"; |
| 1939 | + } |
| 1940 | + if (globalRank == 0 && nbytes > 0) { |
| 1941 | + transport->deregisterBuffer(sendBuffer.get()); |
| 1942 | + } |
| 1943 | + MPI_CHECK(MPI_Barrier(MPI_COMM_WORLD)); |
| 1944 | + } |
| 1945 | + |
| 1946 | + if (globalRank == 0) { |
| 1947 | + CUDACHECK_TEST(cudaMemset(errorCount, 0, sizeof(int))); |
| 1948 | + test::testVerifyTransportStaging( |
| 1949 | + peerTransport, |
| 1950 | + true, |
| 1951 | + 0, |
| 1952 | + perChannelSize, |
| 1953 | + stagingPoison, |
| 1954 | + errorCount, |
| 1955 | + numBlocks, |
| 1956 | + blockSize); |
| 1957 | + CUDACHECK_TEST(cudaDeviceSynchronize()); |
| 1958 | + int hostErrors = 0; |
| 1959 | + CUDACHECK_TEST(cudaMemcpy( |
| 1960 | + &hostErrors, errorCount, sizeof(hostErrors), cudaMemcpyDeviceToHost)); |
| 1961 | + EXPECT_EQ(hostErrors, 0) |
| 1962 | + << "registered send modified transport send staging"; |
| 1963 | + } |
| 1964 | + MPI_CHECK(MPI_Barrier(MPI_COMM_WORLD)); |
| 1965 | +} |
| 1966 | + |
1794 | 1967 | // ============================================================================= |
1795 | 1968 | // Sustained chunked send/recv - repro for the GB200 per-channel DATA_READY |
1796 | 1969 | // deadlock (two NICs atomic-FA the same flag at maxGroups>=8). Streams a large |
|
0 commit comments