移动游戏性能测试工具链:采集、分析、回归与 CI 自动化
·
移动游戏性能测试工具链搭建指南
公开说明:本文为方法论演示稿。项目、场景、设备、平台、路径、端口及性能数据均已重构或合成,不对应任何真实项目、客户或生产环境;代码仅用于说明思路。
脱敏说明:文中的产品、项目、设备、路径、域名与性能数据均已泛化处理,仅用于说明测试方法和分析思路。
概述
手游性能测试不是"开个工具跑一下"那么简单——它需要一套完整的工具链来覆盖采集、监控、分析、报告、回归检测等全链路环节。在示例 UE 项目中的长期测试实践中,测试流程逐步搭建了一套覆盖CPU、GPU、内存、帧率、功耗、温度六大维度的性能测试工具链,实现了从手动测试到自动化流水线的演进。
本文将这套工具链的搭建方法完整分享出来,包括每类工具的选型理由、配置方法、使用技巧和集成方案。目标读者是有一定基础但尚未体系化的性能测试工程师。
一、工具链全景图
┌─────────────────────────────────────────────────────┐
│ 性能测试工具链 │
├──────────┬──────────┬──────────┬──────────┬──────────┤
│ 帧率/CPU │ GPU │ 内存 │ 功耗/温度 │ 系统级 │
├──────────┼──────────┼──────────┼──────────┼──────────┤
│ UE4 Stat │ RenderDoc│memreport │ Battery │ systrace │
│ 示例性能采集工具 │ AGI │ LLM │ Historian│ perfetto │
│ 示例性能监测工具│ 移动 GPU HWC │ MAT │ PowerMon │ simpleperf│
│ dumpsys │ Snapdragon│ dumpsys │ 温度枪 │ strace │
│ gfxinfo │ Profiler│ meminfo │ │ ftrace │
└──────────┴──────────┴──────────┴──────────┴──────────┘
↓ ↓ ↓ ↓
┌─────────────────────────────────────────────────────┐
│ 数据汇聚层 (CSV / JSON / SQLite) │
├─────────────────────────────────────────────────────┐
│ 分析层 (Python / Jupyter) │
├─────────────────────────────────────────────────────┤
│ 报告层 (Markdown / HTML / Grafana) │
└─────────────────────────────────────────────────────┘
二、基础环境准备
2.1 开发机配置
# 推荐配置
# OS: Ubuntu 20.04/22.04 或 Windows + WSL2
# CPU: 8核心以上(数据分析需要)
# RAM: 16GB以上
# 存储: 500GB SSD(性能数据量大)
# 基础软件安装
sudo apt update
sudo apt install -y python3 python3-pip python3-venv adb \
git curl wget unzip
# Python虚拟环境
python3 -m venv ~/perf_env
source ~/perf_env/bin/activate
pip install pandas numpy matplotlib seaborn plotly \
scipy jupyter notebook openpyxl
# ADB配置
sudo apt install android-tools-adb
# 或从Android SDK获取最新版
2.2 测试设备准备
# 查看已连接设备
adb devices
# 设备基础信息采集
adb shell getprop ro.product.model # 设备型号
adb shell getprop ro.build.version.sdk # Android版本
adb shell cat /proc/cpuinfo # CPU信息
adb shell cat /proc/meminfo | head -5 # 内存信息
adb shell getprop ro.hardware.chipname # 芯片型号
# 开发者选项配置
adb shell settings put global development_settings_enabled 1
adb shell settings put system screen_brightness_mode 0 # 关闭自动亮度
adb shell settings put system screen_brightness 128 # 固定亮度
adb shell svc wifi disable # 关闭WiFi(减少干扰)
adb shell svc data disable # 关闭数据(如不需要网络)
2.3 设备信息采集脚本
#!/bin/bash
# collect_device_info.sh - 采集设备基础信息
SERIAL=$(adb get-serialnumber)
OUTPUT="device_info_${SERIAL}.txt"
echo "=== 设备信息采集 ===" > "$OUTPUT"
echo "采集时间: $(date)" >> "$OUTPUT"
echo "" >> "$OUTPUT"
echo "--- 基本信息 ---" >> "$OUTPUT"
echo "型号: $(adb shell getprop ro.product.model)" >> "$OUTPUT"
echo "品牌: $(adb shell getprop ro.product.brand)" >> "$OUTPUT"
echo "Android版本: $(adb shell getprop ro.build.version.release)" >> "$OUTPUT"
echo "SDK版本: $(adb shell getprop ro.build.version.sdk)" >> "$OUTPUT"
echo "CPU ABI: $(adb shell getprop ro.product.cpu.abi)" >> "$OUTPUT"
echo "" >> "$OUTPUT"
echo "--- CPU信息 ---" >> "$OUTPUT"
adb shell cat /proc/cpuinfo >> "$OUTPUT"
echo "" >> "$OUTPUT"
echo "--- 内存信息 ---" >> "$OUTPUT"
adb shell cat /proc/meminfo >> "$OUTPUT"
echo "" >> "$OUTPUT"
echo "--- GPU信息 ---" >> "$OUTPUT"
adb shell dumpsys SurfaceFlinger | grep "GLES:" >> "$OUTPUT"
echo "" >> "$OUTPUT"
echo "--- 屏幕信息 ---" >> "$OUTPUT"
adb shell wm size >> "$OUTPUT"
adb shell wm density >> "$OUTPUT"
echo "设备信息已保存到: $OUTPUT"
三、帧率采集工具
3.1 UE4 Stat命令集
# 在UE4控制台中执行(通过Remote Console或游戏内控制台)
# 基础帧率
stat fps
# 详细帧时间
stat unit # 显示Game线程、Render线程、GPU时间
# 帧率图表
stat fps_chart # 帧率时序图
stat unit_graph # 帧时间时序图
# 启用CSV输出(最推荐)
csvprofile start # 开始录制到CSV
csvprofile stop # 停止录制
# 输出位置: Saved/Profiling/CSV/
3.2 adb gfxinfo 采集
#!/bin/bash
# gfxinfo_collector.sh - 通过gfxinfo采集帧时间
PACKAGE="$1"
DURATION="${2:-60}" # 默认采集60秒
OUTPUT="gfxinfo_$(date +%Y%m%d_%H%M%S).csv"
echo "frame_number,vsync_timestamp,frame_completed,total_time_ms" > "$OUTPUT"
# 重置统计
adb shell dumpsys gfxinfo "$PACKAGE" framestats reset > /dev/null
echo "采集 ${DURATION} 秒的帧数据..."
END_TIME=$(($(date +%s) + DURATION))
while [ $(date +%s) -lt $END_TIME ]; do
# 获取帧统计
STATS=$(adb shell dumpsys gfxinfo "$PACKAGE" framestats 2>/dev/null)
# 解析帧数据行(以数字开头的行)
echo "$STATS" | grep -E "^[0-9]" | while IFS=',' read -ra FIELDS; do
if [ ${#FIELDS[@]} -ge 14 ]; then
START=${FIELDS[1]} # INTENDED_VSYNC
END=${FIELDS[13]} # FRAME_COMPLETED
if [ "$START" -gt 0 ] && [ "$END" -gt "$START" ]; then
TOTAL_NS=$((END - START))
TOTAL_MS=$(echo "scale=2; $TOTAL_NS / 1000000" | bc)
echo "0,$START,$END,$TOTAL_MS" >> "$OUTPUT"
fi
fi
done
# 重置以避免重复计数
adb shell dumpsys gfxinfo "$PACKAGE" framestats reset > /dev/null
sleep 1
done
echo "数据已保存到: $OUTPUT"
echo "总帧数: $(wc -l < $OUTPUT)"
3.3 示例性能采集工具配置
示例性能采集工具代表一类公开第三方采集工具,可按实际环境选择支持免 root 采集的产品。
# 示例性能采集工具 CLI模式使用
# 需要先在PC端登录示例性能采集工具
# 基础采集
示例性能采集工具 -s <serial> -p <package> -o output.csv \
--fps --cpu --memory --gpu --battery --temperature
# 指定采集时长
示例性能采集工具 -s <serial> -p <package> -o output.csv \
--fps --cpu --memory --duration 300
# 导出为标准CSV格式(方便后续Python分析)
示例性能采集工具 -s <serial> -p <package> -o output.csv \
--fps --cpu --memory --format csv
四、GPU性能工具
4.1 RenderDoc(推荐)
# RenderDoc 截帧分析(详见专题文章)
# PC端安装后通过GUI连接Android设备
# 命令行截帧
renderdoccmd capture \
--target-android \
--device <serial> \
--package com.example.demoapp \
--capture-frame 60 \
--output frame_capture.rdcd
# 也可以通过Python脚本自动化
# renderdoc_auto.py - 自动化截帧分析
import renderdoc as rd
def capture_frame(device_serial, package_name, frame_number):
"""通过RenderDoc Python API截帧"""
# 初始化RenderDoc
cap = rd.OpenCaptureFile()
# 配置Android设备
options = rd.ReplayOptions()
options.optimisation = rd.ReplayOptimisationLevel.Fastest
# ... 截帧逻辑
return result
4.2 Android GPU Inspector (AGI)
AGI是Google官方的GPU分析工具,支持Vulkan和GPU Counter。
# 安装AGI后,通过命令行启动
agi -device <serial> -package <package> -api vulkan
# 获取GPU计数器
# 支持的计数器因GPU厂商不同而异
# 移动GPU档位A: GPU Busy, Shader ALU, Texture Fetch, etc.
# 移动GPU档位A: Job Cycles, Fragment Cycles, External Memory Reads, etc.
4.3 UE4 GPU Profiler
# UE4内置GPU分析(需要Development或Debug包)
# 开始GPU分析
profilegpu
# 查看GPU统计
stat gpu
# 查看特定Pass的耗时
stat scenerendering
stat initviews
stat shadows
stat lighting
stat postprocessing
stat translucent
五、内存分析工具
5.1 memreport(详见专题文章)
# UE4 memreport
memreport -full
# 输出位置: Saved/Profiling/MemReports/
# 解析脚本见 memreport 解读文章
5.2 Android系统级内存监控
#!/bin/bash
# mem_monitor.sh - 持续监控内存变化
PACKAGE="$1"
DURATION="${2:-120}"
OUTPUT="meminfo_$(date +%Y%m%d_%H%M%S).csv"
echo "timestamp,pss_total_kb,native_heap_kb,java_heap_kb,graphics_kb,code_kb" > "$OUTPUT"
END_TIME=$(($(date +%s) + DURATION))
while [ $(date +%s) -lt $END_TIME ]; do
TIMESTAMP=$(date +%H:%M:%S)
MEMINFO=$(adb shell dumpsys meminfo "$PACKAGE" 2>/dev/null)
PSS=$(echo "$MEMINFO" | grep "TOTAL PSS:" | awk '{print $3}')
NATIVE=$(echo "$MEMINFO" | grep "Native Heap:" | head -1 | awk '{print $3}')
JAVA=$(echo "$MEMINFO" | grep "Java Heap:" | head -1 | awk '{print $3}')
GRAPHICS=$(echo "$MEMINFO" | grep "Graphics:" | awk '{print $2}')
CODE=$(echo "$MEMINFO" | grep "Code:" | head -1 | awk '{print $2}')
echo "$TIMESTAMP,$PSS,$NATIVE,$JAVA,$GRAPHICS,$CODE" >> "$OUTPUT"
sleep 5
done
echo "内存监控完成: $OUTPUT"
5.3 LLM(Low Level Memory)标签
# UE4 LLM启用
# 在启动参数中添加
-LLM -LLMCSV
# 运行时查看
stat LLM
stat LLMFULL
# 输出CSV格式的内存标签数据
# Saved/Profiling/LLM/
六、功耗与温度监控
6.1 Battery Historian
# 1. 重置电池统计
adb shell dumpsys batterystats --reset
# 2. 执行测试场景(如10分钟游戏)
# 3. 导出电池数据
adb shell dumpsys batterystats > batterystats.txt
adb bugreport > bugreport.zip
# 4. 使用Battery Historian分析
# 在Docker中运行Battery Historian
docker run -p 9999:9999 gcr.io/android-battery-historian/stable:latest
# 5. 访问 http://localhost:9999 上传bugreport.zip
6.2 温度监控
#!/bin/bash
# temp_monitor.sh - 持续监控设备温度
DURATION="${1:-120}"
OUTPUT="temperature_$(date +%Y%m%d_%H%M%S).csv"
echo "timestamp,battery_temp,cpu_temp,gpu_temp" > "$OUTPUT"
END_TIME=$(($(date +%s) + DURATION))
while [ $(date +%s) -lt $END_TIME ]; do
TIMESTAMP=$(date +%H:%M:%S)
# 电池温度
BATTERY=$(adb shell dumpsys battery | grep temperature | awk '{print $2}')
BATTERY_C=$(echo "scale=1; $BATTERY / 10" | bc)
# CPU温度(不同设备路径不同)
CPU_TEMP=""
for path in /sys/class/thermal/thermal_zone0/temp \
/sys/class/thermal/thermal_zone1/temp \
/sys/devices/virtual/thermal/thermal_zone0/temp; do
TEMP=$(adb shell cat $path 2>/dev/null)
if [ -n "$TEMP" ] && [ "$TEMP" -gt 1000 ] 2>/dev/null; then
CPU_C=$(echo "scale=1; $TEMP / 1000" | bc)
CPU_TEMP="$CPU_C"
break
fi
done
echo "$TIMESTAMP,$BATTERY_C,$CPU_TEMP," >> "$OUTPUT"
sleep 10
done
echo "温度监控完成: $OUTPUT"
6.3 功耗估算
#!/usr/bin/env python3
"""基于电池数据估算功耗"""
import subprocess
import time
def get_battery_info():
"""获取电池信息"""
result = subprocess.run(['adb', 'shell', 'dumpsys', 'battery'],
capture_output=True, text=True)
info = {}
for line in result.stdout.split('\n'):
if 'level:' in line:
info['level'] = int(line.split(':')[1].strip())
elif 'temperature:' in line:
info['temperature'] = int(line.split(':')[1].strip()) / 10.0
elif 'voltage:' in line:
info['voltage'] = int(line.split(':')[1].strip()) / 1000.0 # mV -> V
return info
def estimate_power(duration_sec=300, interval_sec=30):
"""估算平均功耗"""
# 获取设备电池容量
cap_result = subprocess.run(['adb', 'shell', 'cat', '/sys/class/power_supply/battery/charge_full'],
capture_output=True, text=True)
battery_capacity_mah = int(cap_result.stdout.strip()) / 1000 # μAh -> mAh
start_battery = get_battery_info()
print(f"开始测试 - 电量: {start_battery['level']}%, 电压: {start_battery.get('voltage', 'N/A')}V")
time.sleep(duration_sec)
end_battery = get_battery_info()
print(f"结束测试 - 电量: {end_battery['level']}%, 电压: {end_battery.get('voltage', 'N/A')}V")
# 计算消耗
level_drop = start_battery['level'] - end_battery['level']
energy_consumed_mah = battery_capacity_mah * level_drop / 100
avg_current_ma = energy_consumed_mah / (duration_sec / 3600)
avg_voltage = (start_battery.get('voltage', 3.8) + end_battery.get('voltage', 3.8)) / 2
avg_power_mw = avg_current_ma * avg_voltage
print(f"\n=== 功耗估算 ===")
print(f"测试时长: {duration_sec}秒")
print(f"电量消耗: {level_drop}% ({energy_consumed_mah:.0f} mAh)")
print(f"平均电流: {avg_current_ma:.0f} mA")
print(f"平均电压: {avg_voltage:.2f} V")
print(f"平均功耗: {avg_power_mw:.0f} mW ({avg_power_mw/1000:.2f} W)")
return {
'avg_current_ma': avg_current_ma,
'avg_power_mw': avg_power_mw,
'level_drop': level_drop
}
if __name__ == '__main__':
estimate_power(duration_sec=300)
七、系统级Tracing工具
7.1 Perfetto(推荐替代Systrace)
# Perfetto是Google的新一代系统追踪工具
# 1. 基础trace采集
adb shell perfetto -o /data/misc/perfetto-traces/trace.pb -t 10s \
sched freq idle am wm gfx view binder_driver hal dalvik camera input res memory
# 2. 拉取trace文件
adb pull /data/misc/perfetto-traces/trace.pb ./trace.pb
# 3. 在 https://ui.perfetto.dev 打开分析
# 4. 使用配置文件进行高级采集
cat > /tmp/perfetto_config.pbtx << 'EOF'
buffers {
size_kb: 65536
fill_policy: RING_BUFFER
}
data_sources {
config {
name: "linux.ftrace"
ftrace_config {
ftrace_events: "sched/sched_switch"
ftrace_events: "power/cpu_frequency"
ftrace_events: "power/suspend_resume"
ftrace_events: "gpu_mem/gpu_mem_total"
}
}
}
data_sources {
config {
name: "linux.process_stats"
process_stats_config {
scan_all_processes_on_start: true
proc_stats_poll_ms: 1000
}
}
}
duration_ms: 30000
EOF
adb push /tmp/perfetto_config.pbtx /data/local/tmp/config.pbtx
adb shell perfetto -c /data/local/tmp/config.pbtx -o /data/misc/perfetto-traces/trace.pb
7.2 simpleperf(CPU 热点分析)
# simpleperf是Android NDK自带的性能分析工具
# 1. 采集CPU热点(需要root或可调试应用)
adb shell simpleperf record -p $(adb shell pidof com.example.demoapp) \
--duration 10 -o /data/local/tmp/perf.data
# 2. 拉取数据
adb pull /data/local/tmp/perf.data ./perf.data
# 3. 生成报告
simpleperf report -i perf.data --sort comm,dso,symbol
# 4. 生成火焰图
simpleperf report -i perf.data -g --symfs ./symbols > report.txt
# 使用 FlameGraph 工具生成可视化
八、工具链集成与自动化
8.1 统一采集框架
#!/usr/bin/env python3
"""
perf_collector.py - 统一性能采集框架
同时采集帧率、CPU、GPU、内存、温度等数据
"""
import subprocess
import threading
import time
import csv
import os
from datetime import datetime
class PerfCollector:
def __init__(self, device_serial, package_name, output_dir='./perf_data'):
self.serial = device_serial
self.package = package_name
self.output_dir = output_dir
self.running = False
os.makedirs(output_dir, exist_ok=True)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
self.csv_file = os.path.join(output_dir, f'perf_{timestamp}.csv')
def _get_memory_info(self):
result = subprocess.run(
['adb', '-s', self.serial, 'shell', 'dumpsys', 'meminfo', self.package],
capture_output=True, text=True, timeout=5
)
pss = native = graphics = 0
for line in result.stdout.split('\n'):
if 'TOTAL PSS:' in line:
parts = line.split()
pss = int(parts[2]) if len(parts) > 2 else 0
elif 'Native Heap:' in line and native == 0:
parts = line.split()
native = int(parts[2]) if len(parts) > 2 else 0
elif 'Graphics:' in line:
parts = line.split()
graphics = int(parts[1]) if len(parts) > 1 else 0
return pss, native, graphics
def _get_temperature(self):
result = subprocess.run(
['adb', '-s', self.serial, 'shell', 'dumpsys', 'battery'],
capture_output=True, text=True, timeout=5
)
for line in result.stdout.split('\n'):
if 'temperature:' in line:
temp = int(line.split(':')[1].strip())
return temp / 10.0
return 0.0
def _get_battery_level(self):
result = subprocess.run(
['adb', '-s', self.serial, 'shell', 'dumpsys', 'battery'],
capture_output=True, text=True, timeout=5
)
for line in result.stdout.split('\n'):
if 'level:' in line:
return int(line.split(':')[1].strip())
return 0
def _get_cpu_usage(self):
result = subprocess.run(
['adb', '-s', self.serial, 'shell', 'top', '-n', '1', '-b'],
capture_output=True, text=True, timeout=5
)
for line in result.stdout.split('\n'):
if self.package in line:
parts = line.split()
for p in parts:
if '%' in p:
return float(p.replace('%', ''))
return 0.0
def _collect_loop(self, interval_sec=5):
with open(self.csv_file, 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow([
'timestamp', 'elapsed_sec', 'pss_kb', 'native_heap_kb',
'graphics_kb', 'cpu_percent', 'temperature_c', 'battery_level'
])
start_time = time.time()
while self.running:
ts = datetime.now().strftime('%H:%M:%S.%3')[:-1]
elapsed = time.time() - start_time
pss, native, graphics = self._get_memory_info()
cpu = self._get_cpu_usage()
temp = self._get_temperature()
battery = self._get_battery_level()
writer.writerow([ts, f'{elapsed:.1f}', pss, native, graphics,
f'{cpu:.1f}', f'{temp:.1f}', battery])
f.flush()
print(f"[{ts}] PSS:{pss}KB CPU:{cpu:.0f}% Temp:{temp:.0f}°C Batt:{battery}%")
time.sleep(interval_sec)
def start(self, interval_sec=5):
"""开始采集"""
self.running = True
self.thread = threading.Thread(target=self._collect_loop, args=(interval_sec,))
self.thread.daemon = True
self.thread.start()
print(f"性能采集已启动,数据保存到: {self.csv_file}")
def stop(self):
"""停止采集"""
self.running = False
self.thread.join(timeout=10)
print(f"性能采集已停止,数据文件: {self.csv_file}")
if __name__ == '__main__':
import sys
serial = sys.argv[1] if len(sys.argv) > 1 else None
package = sys.argv[2] if len(sys.argv) > 2 else 'com.example.demoapp'
if not serial:
result = subprocess.run(['adb', 'devices'], capture_output=True, text=True)
lines = result.stdout.strip().split('\n')[1:]
devices = [l.split('\t')[0] for l in lines if '\tdevice' in l]
if not devices:
print("未检测到设备")
sys.exit(1)
serial = devices[0]
collector = PerfCollector(serial, package)
collector.start(interval_sec=5)
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
collector.stop()
8.2 自动化测试脚本
#!/bin/bash
# auto_perf_test.sh - 自动化性能测试脚本
PACKAGE="com.example.demoapp"
SERIAL=$(adb devices | grep -v "List" | head -1 | awk '{print $1}')
TEST_DURATION=300 # 5分钟
echo "=== 自动化性能测试 ==="
echo "设备: $SERIAL"
echo "包名: $PACKAGE"
echo "时长: $TEST_DURATION 秒"
# 1. 采集设备信息
bash collect_device_info.sh
# 2. 启动统一采集器
python3 perf_collector.py "$SERIAL" "$PACKAGE" &
COLLECTOR_PID=$!
sleep 2
# 3. 冷启动游戏
echo "启动游戏..."
adb -s "$SERIAL" shell am force-stop "$PACKAGE"
sleep 2
adb -s "$SERIAL" shell am start -n "$PACKAGE/com.epicgames.ue4.SplashActivity"
sleep 30 # 等待加载完成
# 4. 采集UE4 memreport
echo "采集memreport..."
adb -s "$SERIAL" shell "echo 'memreport -full' > /dev/null" # 需要实际的控制台通道
sleep 5
# 5. 等待测试完成
echo "测试进行中... (${TEST_DURATION}秒)"
sleep $TEST_DURATION
# 6. 采集结束时的memreport
echo "采集结束memreport..."
# 7. 停止采集器
kill $COLLECTOR_PID
wait $COLLECTOR_PID 2>/dev/null
# 8. 拉取数据
mkdir -p test_results_$(date +%Y%m%d)
adb -s "$SERIAL" pull /sdcard/Android/data/$PACKAGE/files/Saved/Profiling/ \
./test_results_$(date +%Y%m%d)/profiling/
# 9. 生成报告
python3 generate_report.py ./perf_data/
echo "=== 测试完成 ==="
8.3 Jenkins/GitLab CI集成
# .gitlab-ci.yml 示例
performance_test:
stage: test
tags:
- android-device # 标签指定有Android设备的runner
script:
- python3 perf_collector.py "$DEVICE_SERIAL" "$PACKAGE" &
- sleep 5
- adb shell am start -n "$PACKAGE/com.epicgames.ue4.SplashActivity"
- sleep 300
- kill %1
- python3 generate_report.py ./perf_data/
- python3 check_regression.py ./perf_data/ ./baseline/
artifacts:
paths:
- perf_data/
- reports/
expire_in: 30 days
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"' # 定时任务触发
九、设备档位A专项配置
针对设备档位A(入门级 SoC, 移动GPU档位A, 2 GB RAM)的特殊配置:
# 1. 确保足够存储空间(2GB RAM设备存储通常紧张)
adb shell df -h /data
# 2. 清理后台进程
adb shell am kill-all
# 3. 关闭不必要的系统服务
adb shell settings put global auto_time 0
adb shell settings put global auto_time_zone 0
# 4. 移动GPU档位A Counter采集
# 示例GPU-D 支持的Performance Counter
adb shell "echo 1 > /sys/module/mali/parameters/mali_gpu_perf"
# 通过 sysfs 读取GPU利用率
adb shell cat /sys/module/mali/parameters/mali_gpu_utilization
# 5. 设置性能模式(部分某品牌设备支持)
adb shell "echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
# 注意:这会影响功耗测试的真实性,仅用于峰值性能测试
十、数据分析流水线
10.1 Jupyter Notebook模板
# perf_analysis_template.ipynb 的核心cell结构
# Cell 1: 导入与数据加载
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 加载数据
df = pd.read_csv('perf_data/perf_20260101_143000.csv')
print(f"数据量: {len(df)} 条记录,时长: {df['elapsed_sec'].max():.0f} 秒")
# Cell 2: 数据清洗
df = df[df['elapsed_sec'] > 10] # 去掉前10秒
df['fps'] = 1000 / df['frame_time_ms'] if 'frame_time_ms' in df.columns else None
# Cell 3: 基础统计
display(df.describe())
# Cell 4: 多维度时序图
fig, axes = plt.subplots(4, 1, figsize=(16, 16), sharex=True)
axes[0].plot(df['elapsed_sec'], df['fps_smooth'], 'b-')
axes[0].set_ylabel('FPS')
axes[1].plot(df['elapsed_sec'], df['cpu_percent'], 'r-')
axes[1].set_ylabel('CPU %')
axes[2].plot(df['elapsed_sec'], df['pss_kb']/1024, 'g-')
axes[2].set_ylabel('PSS (MB)')
axes[3].plot(df['elapsed_sec'], df['temperature_c'], 'orange')
axes[3].set_ylabel('温度 (°C)')
axes[3].set_xlabel('时间 (秒)')
plt.tight_layout()
plt.savefig('reports/multi_dimension_timeline.png', dpi=150)
# Cell 5: 相关性分析
corr_cols = ['fps', 'cpu_percent', 'pss_kb', 'temperature_c']
if all(c in df.columns for c in corr_cols):
corr = df[corr_cols].corr()
sns.heatmap(corr, annot=True, cmap='coolwarm')
plt.title('指标相关性矩阵')
plt.savefig('reports/correlation_heatmap.png')
10.2 自动化回归检测
#!/usr/bin/env python3
"""性能回归检测:对比当前版本与基线"""
import json
import sys
def load_metrics(perf_dir):
"""从采集数据中提取关键指标"""
# 实际实现中会解析CSV并计算统计指标
return {
'avg_fps': 28.4,
'p50_fps': 30.1,
'p99_frame_time_ms': 67.3,
'jank_rate': 8.72,
'avg_pss_mb': 623,
'peak_pss_mb': 756,
'avg_cpu_percent': 45.2,
'peak_temperature': 42.1
}
def check_regression(baseline_file, current_metrics, thresholds_file='thresholds.json'):
"""检查是否有回归"""
with open(baseline_file) as f:
baseline = json.load(f)
with open(thresholds_file) as f:
thresholds = json.load(f)
issues = []
for metric, threshold in thresholds.items():
if metric not in current_metrics or metric not in baseline:
continue
base_val = baseline[metric]
curr_val = current_metrics[metric]
# 判断方向(FPS类指标越大越好,其他越小越好)
if 'fps' in metric.lower():
regression = (base_val - curr_val) / base_val > threshold
else:
regression = (curr_val - base_val) / base_val > threshold
if regression:
change = (curr_val - base_val) / base_val * 100
issues.append(f"[REGRESSION] {metric}: {base_val:.1f} → {curr_val:.1f} ({change:+.1f}%)")
return issues
if __name__ == '__main__':
thresholds = {
'avg_fps': 0.10,
'p99_frame_time_ms': 0.15,
'jank_rate': 0.20,
'avg_pss_mb': 0.10,
'peak_pss_mb': 0.10,
}
with open('thresholds.json', 'w') as f:
json.dump(thresholds, f, indent=2)
current = load_metrics('./current_perf/')
issues = check_regression('./baseline.json', current)
if issues:
print("❌ 性能回归检测到:")
for issue in issues:
print(f" {issue}")
sys.exit(1)
else:
print("✅ 性能回归检测通过")
sys.exit(0)
总结
手游性能测试工具链的搭建是一个持续演进的过程。核心建议:
- 分层覆盖:帧率→CPU/GPU→内存→功耗/温度→系统级,逐层深入
- 自动化优先:能脚本化的不要手动,能CI集成的不要人工触发
- 数据归档:每次测试的原始数据都要保留,建立版本基线
- 设备矩阵:覆盖高、中、低三个档次的代表设备(如设备档位A代表低端)
- 工具组合:没有万能工具,不同问题用不同工具组合分析
- Python是粘合剂:用Python串联采集、分析、报告的全流程
工具选型推荐清单:
| 维度 | 推荐工具 | 备选工具 |
|---|---|---|
| 帧率 | UE4 CSV Profiler + Python | 示例性能采集工具 |
| GPU | RenderDoc | AGI |
| 内存 | UE4 memreport + dumpsys meminfo | MAT |
| CPU | UE4 Stat + Perfetto | simpleperf |
| 功耗 | Battery Historian | 示例性能采集工具 |
| 温度 | adb dumpsys battery | 温度枪 |
| 系统 | Perfetto | systrace |
标签: #手游性能测试 #工具链 #Android #UE4 #示例性能采集工具 #RenderDoc #Perfetto #自动化测试 #CI/CD #性能优化
更多推荐
所有评论(0)