fix: 速度计算按真实时间间隔而非假定为1秒

calculateSpeed() 原直接取字节差作为 bytes/sec,假定定时器严格
1 秒触发。系统负载高时 QTimer 延迟会导致速度失真。

改为记录上次采样的毫秒时间戳 m_lastTimestampMs,按真实流逝时间
delta_bytes / elapsed_seconds 计算速度;速度存储由 qint64 改为
double 保留精度;elapsedSec > 0 守卫避免除零。

同时新增 docs/project-review.md 项目审查清单。
This commit is contained in:
2026-07-23 00:01:39 +08:00
parent 9f11b32b5e
commit e53981f828
3 changed files with 151 additions and 13 deletions
+24 -11
View File
@@ -22,6 +22,7 @@ NetworkMonitorApplet::NetworkMonitorApplet(QObject *parent)
, m_refreshTimer(nullptr)
, m_lastRxBytes(0)
, m_lastTxBytes(0)
, m_lastTimestampMs(0)
, m_downloadSpeed(0)
, m_uploadSpeed(0)
, m_totalDownload(0)
@@ -79,12 +80,12 @@ bool NetworkMonitorApplet::init()
double NetworkMonitorApplet::downloadSpeed() const
{
return static_cast<double>(m_downloadSpeed);
return m_downloadSpeed;
}
double NetworkMonitorApplet::uploadSpeed() const
{
return static_cast<double>(m_uploadSpeed);
return m_uploadSpeed;
}
double NetworkMonitorApplet::totalDownload() const
@@ -354,32 +355,44 @@ void NetworkMonitorApplet::calculateSpeed()
{
qint64 currentRxBytes = getActiveRxBytes();
qint64 currentTxBytes = getActiveTxBytes();
// 当前采样时间戳(毫秒),用于按真实流逝时间计算速度
const qint64 nowMs = QDateTime::currentMSecsSinceEpoch();
if (m_firstUpdate) {
m_firstUpdate = false;
m_lastRxBytes = currentRxBytes;
m_lastTxBytes = currentTxBytes;
m_lastTimestampMs = nowMs;
return;
}
// 计算速度(字节/秒)
m_downloadSpeed = currentRxBytes - m_lastRxBytes;
m_uploadSpeed = currentTxBytes - m_lastTxBytes;
// 按真实流逝时间计算速度(字节/秒)
// 设计原因:原实现假设定时间隔严格 1 秒,直接取字节差作为速度;
// 系统负载高时 QTimer 可能延迟触发,实际间隔偏离 1 秒导致速度失真。
// 改为除以真实间隔后,无论定时器抖动如何速度都准确。
const double elapsedSec = (nowMs - m_lastTimestampMs) / 1000.0;
// 更新总量
m_totalDownload = currentRxBytes;
m_totalUpload = currentTxBytes;
// 仅当间隔合法时计算速度,避免除零;间隔为 0 时保持上次速度值
if (elapsedSec > 0) {
m_downloadSpeed = (currentRxBytes - m_lastRxBytes) / elapsedSec;
m_uploadSpeed = (currentTxBytes - m_lastTxBytes) / elapsedSec;
}
m_lastRxBytes = currentRxBytes;
m_lastTxBytes = currentTxBytes;
m_lastTimestampMs = nowMs;
// 追加采样点到当前活动接口的环形缓冲
// 设计原因:applet 启动即持续采集,用户随时打开趋势图都能看到完整 5 分钟数据
if (!m_activeInterface.isEmpty()) {
SpeedSample sample;
sample.timestamp = QDateTime::currentSecsSinceEpoch();
sample.downloadSpeed = static_cast<double>(m_downloadSpeed);
sample.uploadSpeed = static_cast<double>(m_uploadSpeed);
sample.downloadSpeed = m_downloadSpeed;
sample.uploadSpeed = m_uploadSpeed;
m_speedHistory[m_activeInterface].append(sample);
// 滑动窗口裁剪:超过 300 点时丢弃最旧的