This article has no English version yet; the original Chinese text follows. Chart labels are in Chinese as well.
代码说明
rate-math.mjs 是可直接导入的纯计算模块,保留整轮速率截断、三轮算术平均和单位转换。它不启动测试,也不读取设备。
measure_pass.h 是按实际计时边界整理的独立 C++17 教学示例。调用方提供本次独占、长度正确的直接 I/O 文件及 64 MiB 对齐缓冲;文件身份检查、预分配、阶段重开、完整校验、资源清理和存档由应用层完成。Completed 表示这一次读写过程完成,整个测试结果的验收在应用层。不要在 UI 线程运行。
正文中的短代码用于解释控制关系,省略了工程上下文;原始内存引擎和专有二进制不包含在这个发布包中。
Files
#pragma once
// Teaching example for one fixed synchronous pass, not the full benchmark.
// Caller owns a validated 1.5 GiB O_DIRECT file and a 64 MiB aligned buffer.
// Caller handles allocation, file identity, preallocation, phase reopen,
// full final-file validation, ownership, cleanup, and history.
#include <atomic>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <limits>
#include <optional>
#include <sys/time.h>
#include <unistd.h>
namespace memstorage_example {
enum class Status { Completed, Stopped, Failed };
struct PassResult {
Status status = Status::Failed;
std::optional<std::int32_t> rateMiBps;
std::optional<double> elapsedMs;
};
inline bool Wall(double& seconds) {
timeval t{};
if (::gettimeofday(&t, nullptr) != 0 || t.tv_sec < 0 ||
t.tv_usec < 0 || t.tv_usec >= 1000000) return false;
seconds = double(t.tv_sec) + double(t.tv_usec) / 1000000.0;
return std::isfinite(seconds);
}
inline PassResult MeasurePass(int fd, void* alignedBuffer, bool writing,
unsigned char pattern, const std::atomic<bool>& stop) {
constexpr std::size_t blockBytes = 64ULL * 1024 * 1024;
constexpr unsigned blocks = 24;
PassResult result;
if (fd < 0 || alignedBuffer == nullptr) return result;
if (stop.load()) { result.status = Status::Stopped; return result; }
if (writing) {
std::memset(alignedBuffer, pattern, blockBytes);
if (::lseek(fd, 0, SEEK_SET) != 0) return result;
}
double begin = 0, end = 0;
if (!Wall(begin)) return result;
for (unsigned i = 0; i < blocks; ++i) {
if (stop.load()) { result.status = Status::Stopped; return result; }
const off_t offset = static_cast<off_t>(i) * static_cast<off_t>(blockBytes);
if (::lseek(fd, offset, SEEK_SET) != offset) return result;
const ssize_t count = writing ? ::write(fd, alignedBuffer, blockBytes)
: ::read(fd, alignedBuffer, blockBytes);
if (count != static_cast<ssize_t>(blockBytes)) return result;
}
if (writing && ::fdatasync(fd) != 0) return result;
if (!Wall(end)) return result;
const double seconds = end - begin;
if (!(seconds > 0.0) || !std::isfinite(seconds)) return result;
const double raw = double(blockBytes) * blocks / (seconds * 1024.0 * 1024.0);
if (!std::isfinite(raw) || raw < 0 || raw > std::numeric_limits<std::int32_t>::max()) return result;
result.status = Status::Completed;
result.elapsedMs = seconds * 1000.0;
result.rateMiBps = static_cast<std::int32_t>(raw);
return result;
}
} // namespace memstorage_example const MIB = 1048576;
export function roundRateMiBps(fileBytes, elapsedSeconds) {
if (!Number.isSafeInteger(fileBytes) || fileBytes <= 0 ||
!Number.isFinite(elapsedSeconds) || elapsedSeconds <= 0) {
throw new RangeError('Positive byte count and finite positive interval required');
}
const raw = fileBytes / MIB / elapsedSeconds;
if (!Number.isFinite(raw) || raw > 2147483647) throw new RangeError('Rate exceeds int32');
return Math.trunc(raw);
}
export function meanThreeRounds(values) {
if (values.length !== 3 || values.some(v => !Number.isInteger(v) || v < 0 || v > 2147483647)) {
throw new RangeError('Three valid integer round rates required');
}
return values.reduce((sum, v) => sum + v, 0) / 3;
}
export function displayRate(mibPerSecond, unit = 'MiB/s') {
if (unit !== 'MiB/s' && unit !== 'MB/s') throw new RangeError('Unknown unit');
if (mibPerSecond === null || !Number.isFinite(mibPerSecond) || mibPerSecond < 0) return null;
return unit === 'MB/s' ? mibPerSecond * 1.048576 : mibPerSecond;
}