已合并
[Fix] 修改 HcommChannelNotifyWaitOnThread 的 timeout 参数单位为:ms->s #4625
Yuanhao Ji创建于 22 天前
[Fix] 修改 HcommChannelNotifyWaitOnThread 的 timeout 参数单位为:ms->s #4625
已合并
Yuanhao Ji创建于 22 天前
3 个文件变更+16-15
@@ -36,7 +36,7 @@ constexpr u32 MEM_BLOCK_SIZE = 128;
36constexpr uint16_t DEFAULT_LISTENING_PORT = 60001;36constexpr uint16_t DEFAULT_LISTENING_PORT = 60001;
37constexpr u32 SEND_RQE_COUNT = 16;37constexpr u32 SEND_RQE_COUNT = 16;
38constexpr u32 HYBRID_QP_STATUS_POLL_SLEEP_US = 1000;38constexpr u32 HYBRID_QP_STATUS_POLL_SLEEP_US = 1000;
39-constexpr u32 DEFAULT_NOTIFY_POLL_TIMEOUT_MS = 30000;39+constexpr u32 DEFAULT_NOTIFY_WAIT_TIMEOUT_S = 30;
40constexpr uint32_t kHostResourceId = 0U;40constexpr uint32_t kHostResourceId = 0U;
41 41 
42static inline CommMemType HcclMemTypeToCommMemType(HcclMemType type)42static inline CommMemType HcclMemTypeToCommMemType(HcclMemType type)
@@ -967,17 +967,17 @@ HcclResult HostCpuRoceChannel::NotifyWait(const uint32_t localNotifyIdx, const u
967 967 
968 // 2.轮询rq_cq968 // 2.轮询rq_cq
969 auto startTime = std::chrono::steady_clock::now();969 auto startTime = std::chrono::steady_clock::now();
970- auto waitTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(timeout));970+ auto waitTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(timeout));
atomgit-bot
atomgit-botatomgit-bot22 天前

🟡 Medium Priority

changed line → affected behavior → failure mode → suggested fix: 第 970 行将 std::chrono::milliseconds(timeout) 改为 std::chrono::seconds(timeout),使非混合模式 NotifyWait 轮询超时上限从毫秒级放大到秒级。但该函数 while (true) 循环内只有 ibv_poll_cq(非阻塞),无任何 sleep/yield(对比本文件 NotifyWaitHybrid 每轮 SaluSleep(pollInterval)),且循环整体位于函数开头 std::lock_guard<std::mutex> lock(cq_mutex)(第 961 行)持锁范围。 后果:对端 notify 迟迟未到时,本端线程 100% CPU 空转并长时间持有 cq_mutex,最坏持续整个超时(默认约 30 分钟)。改动前同值按毫秒解释(约 1.8 秒),问题几乎不可触发;改为秒后缺失退避的忙轮询被真实放大。

建议:在非混合模式 NotifyWait 的轮询循环内加入退避睡眠(参考 NotifyWaitHybrid 的 SaluSleep),避免秒级超时下 100% CPU 空转并长时间持有 cq_mutex。

likedislike
971 for (uint32_t i = 0; i < qpInfo.size(); i++) {971 for (uint32_t i = 0; i < qpInfo.size(); i++) {
972 CHK_PRT_RET(972 CHK_PRT_RET(
973- qpInfo[i].recvCq == nullptr, HCCL_ERROR("[HostCpuRoceChannel::%s] recvCq[%u] is null", i, __func__),973+ qpInfo[i].recvCq == nullptr, HCCL_ERROR("[HostCpuRoceChannel::%s] recvCq[%u] is null", __func__, i),
974 HCCL_E_INTERNAL);974 HCCL_E_INTERNAL);
975 CHK_PRT_RET(975 CHK_PRT_RET(
976- qpInfo[i].qp == nullptr, HCCL_ERROR("[HostCpuRoceChannel::%s] qp[%u] is null", i, __func__),976+ qpInfo[i].qp == nullptr, HCCL_ERROR("[HostCpuRoceChannel::%s] qp[%u] is null", __func__, i),
977 HCCL_E_INTERNAL);977 HCCL_E_INTERNAL);
978 CHK_PRT_RET(978 CHK_PRT_RET(
979 qpInfo[i].recvCq->context == nullptr,979 qpInfo[i].recvCq->context == nullptr,
980- HCCL_ERROR("[HostCpuRoceChannel::%s] recvCq[%u]->context is null", i, __func__), HCCL_E_INTERNAL);980+ HCCL_ERROR("[HostCpuRoceChannel::%s] recvCq[%u]->context is null", __func__, i), HCCL_E_INTERNAL);
981 981 
982 while (true) {982 while (true) {
983 auto actualNum = ibv_poll_cq(qpInfo[i].recvCq, 1, &wc);983 auto actualNum = ibv_poll_cq(qpInfo[i].recvCq, 1, &wc);
@@ -1905,8 +1905,8 @@ HcclResult HostCpuRoceChannel::NotifyWaitHybrid(uint32_t localNotifyIdx, uint32_
1905 1905 
1906 hccl::MemType type = NotifyIdToMemtypeHybird(localNotifyIdx);1906 hccl::MemType type = NotifyIdToMemtypeHybird(localNotifyIdx);
1907 1907 
1908- // 使用配置的超时时间和轮询间隔1908+ // 使用配置的超时时间和轮询间隔(timeOut单位为秒,默认30s)
1909- uint32_t pollTimeout = (timeout == 0) ? DEFAULT_NOTIFY_POLL_TIMEOUT_MS : timeout;1909+ uint32_t pollTimeout = (timeout == 0) ? DEFAULT_NOTIFY_WAIT_TIMEOUT_S : timeout;
1910 uint32_t pollInterval = 1;1910 uint32_t pollInterval = 1;
1911 1911 
1912 // 使用原子操作读取 Notify 内存1912 // 使用原子操作读取 Notify 内存
@@ -1914,7 +1914,7 @@ HcclResult HostCpuRoceChannel::NotifyWaitHybrid(uint32_t localNotifyIdx, uint32_
1914 const uint64_t expectedValue = 1;1914 const uint64_t expectedValue = 1;
1915 1915 
1916 auto startTime = std::chrono::steady_clock::now();1916 auto startTime = std::chrono::steady_clock::now();
1917- auto waitTime = std::chrono::milliseconds(pollTimeout);1917+ auto waitTime = std::chrono::seconds(pollTimeout);
1918 1918 
1919 while (true) {1919 while (true) {
1920 // 使用原子操作读取,确保内存可见性1920 // 使用原子操作读取,确保内存可见性
@@ -38,6 +38,7 @@ namespace hcomm {
38constexpr u32 MEM_BLOCK_SIZE = 128;38constexpr u32 MEM_BLOCK_SIZE = 128;
39constexpr uint16_t DEFAULT_LISTENING_PORT = 60001;39constexpr uint16_t DEFAULT_LISTENING_PORT = 60001;
40constexpr u32 SEND_RQE_COUNT = 16;40constexpr u32 SEND_RQE_COUNT = 16;
41+constexpr u32 DEFAULT_NOTIFY_WAIT_TIMEOUT_S = 30; // NotifyWait超时默认值(单位:秒)
41 42 
42HostCpuRoceChannel::HostCpuRoceChannel(EndpointHandle endpointHandle, HcommChannelDesc channelDesc)43HostCpuRoceChannel::HostCpuRoceChannel(EndpointHandle endpointHandle, HcommChannelDesc channelDesc)
43 : endpointHandle_(endpointHandle),44 : endpointHandle_(endpointHandle),
@@ -898,7 +899,7 @@ HcclResult HostCpuRoceChannel::NotifyWait(const uint32_t localNotifyIdx, const u
898 899 
899 // 2.轮询rq_cq900 // 2.轮询rq_cq
900 auto startTime = std::chrono::steady_clock::now();901 auto startTime = std::chrono::steady_clock::now();
901- auto waitTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(timeout));902+ auto waitTime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(timeout));
atomgit-bot
atomgit-botatomgit-bot22 天前

🟡 Medium Priority

changed line → affected behavior → failure mode → suggested fix: 第 902 行将 std::chrono::milliseconds(timeout) 改为 std::chrono::seconds(timeout),使非混合模式 NotifyWait 的轮询超时上限从毫秒级放大到秒级(调用方 HcommChannelNotifyWaitOnThread 传入的 timeOut 单位本就是秒,默认 NOTIFY_WAIT_TIMEOUT_DEFAULT = 1836 秒)。但该函数的 while (true) 循环内只有 ibv_poll_cq(非阻塞),没有任何 sleep/yield(对比 NotifyWaitHybrid 中每轮有 SaluSleep(pollInterval)),且整个循环在函数开头 std::lock_guard<std::mutex> lock(cq_mutex)(第 893 行)的持锁范围内。 后果:当对端 notify 迟迟未到达(对端仍在计算,或发生死锁)时,本端线程会以 100% CPU 空转、并长时间持有 cq_mutex,最坏持续到整个超时(默认约 30 分钟),阻塞其他需要该 CQ 的线程。改动前同样的值被按毫秒解释(约 1.8 秒),此问题几乎不可触发;改为秒后该缺失 backoff 的忙轮询问题被真实放大。

建议:在非混合模式 NotifyWait 的轮询循环内加入退避睡眠(参考 NotifyWaitHybrid 的 SaluSleep),避免秒级超时下 100% CPU 空转并长时间持有 cq_mutex。

likedislike
902 for (uint32_t i = 0; i < qpInfo.size(); i++) {903 for (uint32_t i = 0; i < qpInfo.size(); i++) {
903 CHK_PRT_RET(904 CHK_PRT_RET(
904 qpInfo[i].recvCq == nullptr, HCCL_ERROR("[HostCpuRoceChannel::%s] recvCq[%u] is null", __func__, i),905 qpInfo[i].recvCq == nullptr, HCCL_ERROR("[HostCpuRoceChannel::%s] recvCq[%u] is null", __func__, i),
@@ -1848,8 +1849,8 @@ HcclResult HostCpuRoceChannel::NotifyWaitHybrid(uint32_t localNotifyIdx, uint32_
1848 1849 
1849 hccl::MemType type = NotifyIdToMemtypeHybird(localNotifyIdx);1850 hccl::MemType type = NotifyIdToMemtypeHybird(localNotifyIdx);
1850 1851 
1851- // 使用配置的超时时间和轮询间隔1852+ // 使用配置的超时时间和轮询间隔(timeOut单位为秒,默认30s)
1852- uint32_t pollTimeout = (timeout == 0) ? 30000 : timeout;1853+ uint32_t pollTimeout = (timeout == 0) ? DEFAULT_NOTIFY_WAIT_TIMEOUT_S : timeout;
1853 uint32_t pollInterval = 1;1854 uint32_t pollInterval = 1;
1854 1855 
1855 // 使用原子操作读取 Notify 内存1856 // 使用原子操作读取 Notify 内存
@@ -1857,7 +1858,7 @@ HcclResult HostCpuRoceChannel::NotifyWaitHybrid(uint32_t localNotifyIdx, uint32_
1857 const uint64_t expectedValue = 1;1858 const uint64_t expectedValue = 1;
1858 1859 
1859 auto startTime = std::chrono::steady_clock::now();1860 auto startTime = std::chrono::steady_clock::now();
1860- auto waitTime = std::chrono::milliseconds(pollTimeout);1861+ auto waitTime = std::chrono::seconds(pollTimeout);
1861 1862 
1862 while (true) {1863 while (true) {
1863 // 使用原子操作读取,确保内存可见性1864 // 使用原子操作读取,确保内存可见性
@@ -1289,8 +1289,8 @@ TEST_F(HostCpuRoceChannelTest, Ut_NotifyWaitHybrid_Timeout_Expect_HCCL_E_TIMEOUT
1289 impl_->localMemMsg_[hccl::DATA_NOTIFY_MEM].len = sizeof(std::atomic<uint32_t>);1289 impl_->localMemMsg_[hccl::DATA_NOTIFY_MEM].len = sizeof(std::atomic<uint32_t>);
1290 impl_->localMemMsg_[hccl::DATA_NOTIFY_MEM].lkey = 0;1290 impl_->localMemMsg_[hccl::DATA_NOTIFY_MEM].lkey = 0;
1291 1291 
1292- // 使用很短的超时时间(1ms)触发超时1292+ // 使用很短的超时时间(1s)触发超时
1293- HcclResult ret = impl_->NotifyWaitHybrid(1, 1); // 1ms 超时1293+ HcclResult ret = impl_->NotifyWaitHybrid(1, 1); // 1s 超时
1294 EXPECT_EQ(ret, HCCL_E_TIMEOUT);1294 EXPECT_EQ(ret, HCCL_E_TIMEOUT);
1295 1295 
1296 impl_->isHybridMode_ = false; // 析构前设置为false避免调用HrtRaMrDereg1296 impl_->isHybridMode_ = false; // 析构前设置为false避免调用HrtRaMrDereg
@@ -1314,7 +1314,7 @@ TEST_F(HostCpuRoceChannelTest, Ut_NotifyWaitHybrid_NullMemAddr_Expect_HCCL_E_TIM
1314 impl_->localMemMsg_[hccl::ACK_NOTIFY_MEM].len = sizeof(std::atomic<uint32_t>);1314 impl_->localMemMsg_[hccl::ACK_NOTIFY_MEM].len = sizeof(std::atomic<uint32_t>);
1315 impl_->localMemMsg_[hccl::ACK_NOTIFY_MEM].lkey = 0;1315 impl_->localMemMsg_[hccl::ACK_NOTIFY_MEM].lkey = 0;
1316 1316 
1317- // 使用1ms超时1317+ // 使用1s超时
1318 HcclResult ret = impl_->NotifyWaitHybrid(0, 1);1318 HcclResult ret = impl_->NotifyWaitHybrid(0, 1);
1319 EXPECT_EQ(ret, HCCL_E_TIMEOUT);1319 EXPECT_EQ(ret, HCCL_E_TIMEOUT);
1320 1320