← MemStorageBench: measuring memory bandwidth and storage throughput on HarmonyOS

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

measure_pass.h 62 lines Download ↓
#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
rate-math.mjs 22 lines Download ↓
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;
}