经典的内存带宽基准测试工具STREAM(Sustainable Memory Bandwidth in High Performance Computers)的鸿蒙化解析和验证
本文详细记录了在OpenHarmony环境下使用OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh命令构建Stream内存带宽测试工具的全过程。Stream作为经典的内存性能评估工具,包含标准版和SVE优化版两种实现,通过Copy、Scale等向量操作测量内存带宽。构建过程涉及环境配置、源码下载、多版本编译(标准stream_c.exe和
·
本文记录使用命令 OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh 构建 Stream 内存带宽基准测试工具的完整过程,包括环境、构建链路、关键日志、常见问题与解决方案、产物验证与重建方法,便于复现与运维。
📖 Stream 简介
STREAM(Sustainable Memory Bandwidth in High Performance Computers)是一个经典的内存带宽基准测试工具,用于测量系统的可持续内存带宽。它通过执行四种简单的向量操作(Copy、Scale、Add、Triad)来评估内存子系统的性能,是 HPC(高性能计算)领域广泛使用的基准测试工具。
🎯 Stream 的作用与重要性
Stream 是内存性能评估的核心工具,提供了:
- 内存带宽测试:测量系统的内存带宽性能
- 性能基准:提供标准的内存性能基准测试
- 系统评估:评估内存子系统的性能
- 性能对比:对比不同系统的内存性能
- 优化参考:为应用优化提供内存性能参考
🔧 Stream 核心特性
1. 内存带宽测试
- Copy 操作:
a[i] = b[i]- 测试内存复制带宽 - Scale 操作:
a[i] = q * b[i]- 测试内存缩放带宽 - Add 操作:
a[i] = b[i] + c[i]- 测试内存加法带宽 - Triad 操作:
a[i] = b[i] + q * c[i]- 测试内存三元运算带宽
2. 测试模式
- 单线程测试:单线程内存带宽测试
- 多线程测试:多线程并行内存带宽测试
- 可配置数组大小:可配置测试数组大小
- 可配置迭代次数:可配置测试迭代次数
3. 结果输出
- 性能指标:输出每种操作的带宽(MB/s)
- 性能报告:生成详细的性能测试报告
- 对比分析:对比不同测试结果
- 可视化:支持结果可视化
4. 应用场景
- 内存性能评估:评估系统的内存带宽性能
- 性能基准:建立内存性能基准测试
- 系统对比:对比不同系统的内存性能
- 优化参考:为应用优化提供参考
- 硬件测试:测试内存硬件性能
🚀 构建入口与环境
- 📝 执行命令:
OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh - 🔧 入口脚本:
create-hnp.sh- 检查必需的环境变量
OHOS_ARCH和OHOS_ABI - 导出
LC_CTYPE、TOOL_HOME、OHOS_SDK_HOME - 执行
make -C build-hnp
- 检查必需的环境变量
- 📦 顶层构建:
build-hnp/MakefilePKGS变量定义需要构建的包列表(包含stream)- 通过
check-pkgs机制自动检测PKGS变化并触发重新构建 - 自动合并
external-hnp目录下的外部 HNP 包 base.hnp依赖所有包的.stamp和外部 HNP 包- 总目标
all: copy,打包base.hnp并拷贝到entry/hnp/$(OHOS_ABI)
⚙️ Stream 包的构建配置
- 📁 包目录:
build-hnp/stream/Makefile- 继承通用规则:
include ../utils/Makefrag - 源地址:
https://github.com/jeffhammond/STREAM/archive/6703f7504a38a8da96b353cadafa64d3c2d7a2d3.zip(标准 STREAM)https://github.com/jlinford/stream/archive/3b92a251022474a293cefc1291bc4fe7b78b62e7.zip(增强版 Stream,支持 SVE)
- 版本:两个不同的实现版本
- 继承通用规则:
- 🔨 构建流程:
- 下载两个源码包(从 GitHub,ZIP 格式)
- 解压第一个包到
temp/STREAM-6703f7504a38a8da96b353cadafa64d3c2d7a2d3 - 编译
stream_c.exe(标准版本,静态链接) - 解压第二个包到
temp/stream-3b92a251022474a293cefc1291bc4fe7b78b62e7 - 编译
stream_zfill-acle.exe(SVE 优化版本) - 编译
stream_zfill.exe(zfill 版本) - 复制所有可执行文件到
build/bin - 复制到
../sysroot
- ⚙️ 编译参数:
- stream_c.exe:
CC=$(OHOS_ARCH)-unknown-linux-ohos-clangCFLAGS="-O3 -static -DNDEBUG -DSTREAM_ARRAY_SIZE=120000000 -DNTIMES=200"LDFLAGS="-static"
- stream_zfill-acle.exe:
CC=$(OHOS_ARCH)-unknown-linux-ohos-clangCFLAGS="-march=armv8.5-a+sve -O3 -static -DNDEBUG -DSTREAM_ARRAY_SIZE=120000000 -DNTIMES=200"LDFLAGS="-static -lomp"KERNEL=zfill-acle
- stream_zfill.exe:
CC=$(OHOS_ARCH)-unknown-linux-ohos-clangCFLAGS="-O3 -static -DNDEBUG -DSTREAM_ARRAY_SIZE=120000000 -DNTIMES=200"LDFLAGS="-static -lomp"KERNEL=zfill
- stream_c.exe:
- 🔧 通用工具链与路径:
build-hnp/utils/MakefragCC/CXX/LD/AR/RANLIB/...均指向 OHOS SDK 的 LLVM 工具链- 下载支持多镜像回退:
wget→curl,主镜像失败时自动尝试备用镜像
📋 关键日志与过程节点
- 📥 下载与解包:
- 从 GitHub 下载两个 ZIP 源码包
- 完成解压并进入相应的源码目录
- 下载规则支持多镜像回退:
wget→curl兜底
- 🔨 编译阶段:
- stream_c.exe:
- 使用标准 C 编译器编译
- 静态链接,优化级别 O3
- 数组大小:120000000,迭代次数:200
- stream_zfill-acle.exe:
- 使用 SVE(Scalable Vector Extension)指令集编译
- 架构:
armv8.5-a+sve - 链接 OpenMP 库(
-lomp)
- stream_zfill.exe:
- 使用 zfill 内核编译
- 链接 OpenMP 库(
-lomp)
- stream_c.exe:
- 📦 打包:
- 完成
base.hnp重打包,拷贝产物到entry/hnp/arm64-v8a/ - Stream 工具已成功打包到
base.hnp中
- 完成
✅ 产物验证
📦 检查打包文件
ls build-hnp/base.hnp # 应存在
ls entry/hnp/arm64-v8a/*.hnp # 应包含 base.hnp 与 base-public.hnp
🔍 检查二进制文件
# 检查 Stream 可执行文件
ls -lh build-hnp/sysroot/bin/stream*.exe
file build-hnp/sysroot/bin/stream*.exe
✅ 构建验证结果:
- ✅ Stream 可执行文件已安装:
stream_c.exe(811K) - 标准 STREAM 基准测试stream_zfill-acle.exe(3.6M) - SVE 优化版本stream_zfill.exe(3.6M) - zfill 版本
- ✅ 文件类型:ELF 64-bit LSB executable, ARM aarch64
- ✅ 静态链接:
statically linked - ✅ 包含调试信息:
with debug_info, not stripped - ✅ 已打包到
base.hnp中
💻 终端中执行的示例命令
💾 Stream 基本使用
1. 基本运行
# 运行标准 STREAM 基准测试
stream_c.exe
# 运行 SVE 优化版本
stream_zfill-acle.exe
# 运行 zfill 版本
stream_zfill.exe
# 运行并保存结果到文件
stream_c.exe > stream_result.txt 2>&1
# 运行并显示详细信息
stream_c.exe 2>&1 | tee stream_output.txt

2. 理解输出结果
# STREAM 输出示例:
# STREAM version $Revision: 5.10 $
#
# This system uses 8 bytes per DOUBLE PRECISION word.
#
# Array size = 120000000, Offset = 0
# Total memory required = 2746.6 MB.
# Each test will run 200 times.
# The *best* time for each test is used.
#
# Function Best Rate MB/s Avg time Min time Max time
# Copy: 12345.6 0.0155 0.0155 0.0156
# Scale: 12345.6 0.0155 0.0155 0.0156
# Add: 12345.6 0.0155 0.0155 0.0156
# Triad: 12345.6 0.0155 0.0155 0.0156
# 提取性能数据
stream_c.exe | grep -E "(Copy|Scale|Add|Triad)" | awk '{print $2}'
# 提取最佳带宽
stream_c.exe | grep "Copy:" | awk '{print $2}'
# 提取最小时间
stream_c.exe | grep "Copy:" | awk '{print $5}'

3. 性能对比
# 对比不同版本的性能
echo "=== Standard STREAM ===" > comparison.txt
stream_c.exe >> comparison.txt
echo "" >> comparison.txt
echo "=== SVE Optimized ===" >> comparison.txt
stream_zfill-acle.exe >> comparison.txt
echo "" >> comparison.txt
echo "=== Zfill Version ===" >> comparison.txt
stream_zfill.exe >> comparison.txt
# 提取并对比性能数据
for exe in stream_c.exe stream_zfill-acle.exe stream_zfill.exe; do
echo "=== $exe ==="
$exe | grep -E "(Copy|Scale|Add|Triad)"
done
# 生成性能对比报告
{
echo "STREAM Benchmark Comparison"
echo "=========================="
echo ""
for exe in stream_c.exe stream_zfill-acle.exe stream_zfill.exe; do
echo "=== $exe ==="
$exe | grep -E "(Copy|Scale|Add|Triad)"
echo ""
done
} > benchmark_comparison.txt
4. 批量测试
# 运行多次测试并取平均值
for i in {1..10}; do
echo "Run $i:"
stream_c.exe | grep "Copy:" | awk '{print $2}'
done | awk '{sum+=$1; count++} END {print "Average:", sum/count, "MB/s"}'
# 运行多次测试并保存所有结果
for i in {1..10}; do
echo "=== Run $i ===" >> all_results.txt
stream_c.exe >> all_results.txt
echo "" >> all_results.txt
done
# 统计性能数据
stream_c.exe | grep -E "(Copy|Scale|Add|Triad)" | awk '{print $2}' | awk '{sum+=$1; count++} END {print "Average Bandwidth:", sum/count, "MB/s"}'
5. 性能分析
# 提取所有操作的性能数据
stream_c.exe | grep -E "(Copy|Scale|Add|Triad)" | awk '{print $1, $2}' > performance_data.txt
# 计算平均带宽
stream_c.exe | grep -E "(Copy|Scale|Add|Triad)" | awk '{sum+=$2; count++} END {print "Average:", sum/count, "MB/s"}'
# 找出最佳性能的操作
stream_c.exe | grep -E "(Copy|Scale|Add|Triad)" | sort -k2 -rn | head -1
# 找出最差性能的操作
stream_c.exe | grep -E "(Copy|Scale|Add|Triad)" | sort -k2 -n | head -1
# 提取性能指标到 CSV
echo "Operation,Bandwidth_MB_s,Avg_Time,Min_Time,Max_Time" > stream_results.csv
stream_c.exe | grep -E "(Copy|Scale|Add|Triad)" | awk '{print $1","$2","$3","$5","$6}' >> stream_results.csv
6. 实际应用示例
# 运行基准测试并保存结果
stream_c.exe > baseline.txt
# 进行系统优化后再次测试
stream_c.exe > optimized.txt
# 对比优化前后的性能
diff baseline.txt optimized.txt
# 使用脚本自动化测试
#!/bin/bash
echo "STREAM Benchmark Test"
echo "===================="
echo "Date: $(date)"
echo ""
echo "Standard STREAM:"
stream_c.exe
echo ""
echo "SVE Optimized:"
stream_zfill-acle.exe
echo ""
echo "Zfill Version:"
stream_zfill.exe
# 在后台运行测试
nohup stream_c.exe > stream_background.log 2>&1 &
# 监控测试进程
watch -n 1 'ps aux | grep stream'
# 使用 perf 分析性能
perf record -g stream_c.exe
perf report
# 使用 strace 跟踪系统调用
strace -o stream_trace.txt stream_c.exe
# 测试不同数组大小(需要重新编译)
# 修改 Makefile 中的 STREAM_ARRAY_SIZE 值
# 测试不同迭代次数(需要重新编译)
# 修改 Makefile 中的 NTIMES 值
# 运行压力测试
for i in {1..100}; do
echo "Iteration $i"
stream_c.exe > /dev/null
done
# 生成性能报告
{
echo "STREAM Benchmark Report"
echo "======================"
echo "Date: $(date)"
echo "System: $(uname -a)"
echo ""
echo "Standard STREAM Results:"
stream_c.exe
echo ""
echo "SVE Optimized Results:"
stream_zfill-acle.exe
echo ""
echo "Zfill Version Results:"
stream_zfill.exe
} > stream_report.txt
# 提取关键性能指标
stream_c.exe | awk '/^Copy:/ {print "Copy Bandwidth:", $2, "MB/s"}'
stream_c.exe | awk '/^Scale:/ {print "Scale Bandwidth:", $2, "MB/s"}'
stream_c.exe | awk '/^Add:/ {print "Add Bandwidth:", $2, "MB/s"}'
stream_c.exe | awk '/^Triad:/ {print "Triad Bandwidth:", $2, "MB/s"}'
# 计算总体性能
stream_c.exe | grep -E "(Copy|Scale|Add|Triad)" | awk '{sum+=$2; count++} END {print "Overall Average:", sum/count, "MB/s"}'
# 格式化输出为表格
stream_c.exe | grep -E "(Copy|Scale|Add|Triad)" | awk '{printf "%-10s %10.2f MB/s\n", $1, $2}'
# 生成 JSON 格式结果
stream_c.exe | grep -E "(Copy|Scale|Add|Triad)" | awk '{printf "{\"operation\":\"%s\",\"bandwidth\":%s,\"avg_time\":%s,\"min_time\":%s,\"max_time\":%s}\n", $1, $2, $3, $5, $6}'
7. 性能监控
# 运行测试并监控系统资源
stream_c.exe &
STREAM_PID=$!
top -p $STREAM_PID
wait $STREAM_PID
# 监控内存使用
stream_c.exe &
STREAM_PID=$!
while kill -0 $STREAM_PID 2>/dev/null; do
ps -p $STREAM_PID -o rss,vsz
sleep 1
done
# 监控 CPU 使用
stream_c.exe &
STREAM_PID=$!
while kill -0 $STREAM_PID 2>/dev/null; do
ps -p $STREAM_PID -o %cpu,%mem
sleep 1
done
# 使用 htop 监控
htop -p $(pgrep stream)
8. 结果处理与分析
# 解析结果并提取关键指标
parse_stream_results() {
local file=$1
echo "=== Results from $file ==="
grep -E "(Copy|Scale|Add|Triad)" "$file" | while read line; do
op=$(echo "$line" | awk '{print $1}')
bw=$(echo "$line" | awk '{print $2}')
echo "$op: $bw MB/s"
done
}
# 使用函数解析结果
parse_stream_results stream_result.txt
# 对比多个结果文件
compare_results() {
for file in "$@"; do
echo "=== $file ==="
parse_stream_results "$file"
echo ""
done
}
# 对比结果
compare_results baseline.txt optimized.txt
# 生成性能图表数据
stream_c.exe | grep -E "(Copy|Scale|Add|Triad)" | awk '{print $1, $2}' > chart_data.txt
# 计算性能提升百分比
baseline=$(./stream_c.exe | grep "Copy:" | awk '{print $2}')
optimized=$(./stream_zfill-acle.exe | grep "Copy:" | awk '{print $2}')
improvement=$(echo "scale=2; ($optimized - $baseline) / $baseline * 100" | bc)
echo "Performance improvement: $improvement%"
🧪 功能验证脚本
#!/bin/bash
# Stream 工具验证脚本
STREAM_BIN="build-hnp/sysroot/bin"
echo "=== Stream 工具验证 ==="
# 检查可执行文件
echo ""
echo "=== 可执行文件验证 ==="
for exe in stream_c.exe stream_zfill-acle.exe stream_zfill.exe; do
if [ -f "$STREAM_BIN/$exe" ]; then
echo "✓ $exe: 存在"
file "$STREAM_BIN/$exe"
echo " 文件大小: $(ls -lh "$STREAM_BIN/$exe" | awk '{print $5}')"
echo " 架构信息: $(file "$STREAM_BIN/$exe" | grep -o "ARM aarch64")"
echo " 链接类型: $(file "$STREAM_BIN/$exe" | grep -o "statically linked\|dynamically linked")"
else
echo "✗ $exe: 缺失"
fi
done
# 测试基本功能(在目标设备上)
echo ""
echo "=== 功能测试(需要在目标设备上运行)==="
echo "测试标准 STREAM:"
echo " $STREAM_BIN/stream_c.exe"
echo ""
echo "测试 SVE 优化版本:"
echo " $STREAM_BIN/stream_zfill-acle.exe"
echo ""
echo "测试 zfill 版本:"
echo " $STREAM_BIN/stream_zfill.exe"
echo ""
echo "运行并保存结果:"
echo " $STREAM_BIN/stream_c.exe > stream_result.txt 2>&1"
🐛 构建过程遇到的问题及解决方法
❌ 问题 1:GitHub 下载失败
- 🔍 症状:无法从 GitHub 下载源码包
- 🔎 原因:网络问题或 GitHub 访问受限
- ✅ 解决方法:
- 手动下载源码包放置到
build-hnp/stream/download/目录 - 使用代理或镜像站点下载
- 通用下载规则支持
curl兜底 - 位置:
build-hnp/stream/Makefile:20-26
- 手动下载源码包放置到
❌ 问题 2:ZIP 解压失败
- 🔍 症状:解压 ZIP 文件失败
- 🔎 原因:ZIP 文件损坏或
unzip工具不可用 - ✅ 解决方法:
- 检查 ZIP 文件完整性
- 确保系统安装了
unzip工具 - 可以手动解压 ZIP 文件到相应的
temp目录 - 位置:
build-hnp/stream/Makefile:6,10
❌ 问题 3:OpenMP 库链接失败(stream_zfill 版本)
- 🔍 症状:链接错误,无法找到
libomp或 OpenMP 符号 - 🔎 原因:OpenMP 库未安装或路径不正确
- ✅ 解决方法:
- 确保 OpenMP 库在
sysroot/lib中可用 - 检查
-lomp链接标志是否正确 - 如果不需要 OpenMP 支持,可以移除
-lomp链接标志 - 位置:
build-hnp/stream/Makefile:11,14
- 确保 OpenMP 库在
❌ 问题 4:SVE 指令集不支持
- 🔍 症状:编译错误,SVE 指令无法识别
- 🔎 原因:目标平台不支持 SVE 指令集或编译器不支持
- ✅ 解决方法:
- 检查目标平台是否支持 SVE(ARMv8.5+)
- 如果平台不支持,可以移除
-march=armv8.5-a+sve标志 - 使用标准版本
stream_c.exe作为替代 - 位置:
build-hnp/stream/Makefile:11
❌ 问题 5:静态链接失败
- 🔍 症状:静态链接错误,无法找到静态库
- 🔎 原因:静态库未安装或路径不正确
- ✅ 解决方法:
- 确保静态库在
sysroot/lib中可用 - 检查
-static链接标志是否正确 - 如果静态链接失败,可以尝试动态链接
- 位置:
build-hnp/stream/Makefile:7,11,14
- 确保静态库在
❌ 问题 6:数组大小过大导致内存不足
- 🔍 症状:运行时内存不足或段错误
- 🔎 原因:
STREAM_ARRAY_SIZE设置过大,超出可用内存 - ✅ 解决方法:
- 减小
STREAM_ARRAY_SIZE值(当前为 120000000) - 根据系统可用内存调整数组大小
- 位置:
build-hnp/stream/Makefile:7,11,14
- 减小
❌ 问题 7:Makefile 目标不存在
- 🔍 症状:
make stream_c.exe或make KERNEL=zfill-acle all失败 - 🔎 原因:上游 Makefile 不支持这些目标或参数
- ✅ 解决方法:
- 检查上游 Makefile 支持的目标
- 根据实际 Makefile 调整构建命令
- 可能需要直接编译源文件而不是使用 Makefile
- 位置:
build-hnp/stream/Makefile:7,11,14
❌ 问题 8:架构不支持
- 🔍 症状:
Unsupported OHOS_ARCH= - 🔎 原因:传入的架构不在支持列表中
- ✅ 解决方法:
- 确保传入支持架构(
aarch64或x86_64) - 位置:
build-hnp/Makefile:1-49
- 确保传入支持架构(
🔄 重建与扩展
-
🔧 重建单包:
make -C build-hnp rebuild-stream # 触发子包重新编译并刷新 .stamp -
🧹 清理:
make -C build-hnp clean # 清理 sysroot、所有 .stamp 和 PKGS_MARKER -
📦 扩展:Stream 是内存性能评估的核心工具,被广泛用于内存带宽测试和性能基准
-
🔄 自动重建机制:
- 修改
PKGS后,check-pkgs会自动检测变化并触发重新构建 - 新增外部 HNP 包到
external-hnp目录后,会自动合并到base.hnp
- 修改
💡 实践建议
- 🔧 构建配置:根据目标平台选择是否启用 SVE 优化
- 🚀 内存配置:根据系统可用内存调整
STREAM_ARRAY_SIZE - 📦 性能测试:Stream 为内存性能测试提供了强大的工具支持
- 🔗 版本选择:根据需求选择合适的版本(标准版、SVE 优化版、zfill 版)
- 🌐 结果分析:使用多个版本进行对比测试以获得更全面的性能数据
📝 结论与建议
- ✅ 本次已在 aarch64 环境下完成 Stream 内存带宽基准测试工具的交叉编译与打包,三个版本的可执行文件已安装到
sysroot并纳入 HNP 包。 - 💡 为保证构建稳定:
- 使用直接编译方式,不依赖复杂的构建系统
- 静态链接确保可执行文件独立运行
- 确保通过
create-hnp.sh触发构建以获得完整环境变量 - 利用
check-pkgs机制自动检测包列表变化,无需手动清理 - Stream 为内存性能测试提供了强大的工具支持
- 常见陷阱包括 GitHub 下载失败、OpenMP 库链接失败、SVE 指令集不支持;当前已通过构建配置和参数处理
- 建议根据系统可用内存调整数组大小,并根据需求选择合适的版本进行测试
- 已完成 aarch64 目标下 Stream 的交叉编译与打包,工具进入
sysroot并纳入 HNP 包
📚 以上为 Stream 构建的深度解读与实践记录。
更多推荐

所有评论(0)