150 lines
4.2 KiB
Python
150 lines
4.2 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""
|
|||
|
|
昇腾 310B4 NPU 推理性能基准测试
|
|||
|
|
测试项目:
|
|||
|
|
1. 确认 NPU 推理(非 CPU fallback)
|
|||
|
|
2. 单次推理延迟
|
|||
|
|
3. 连续推理吞吐 (FPS)
|
|||
|
|
4. 多 worker 并发
|
|||
|
|
5. 不同分辨率影响
|
|||
|
|
"""
|
|||
|
|
import os
|
|||
|
|
import sys
|
|||
|
|
import time
|
|||
|
|
import socket
|
|||
|
|
import struct
|
|||
|
|
import json
|
|||
|
|
import base64
|
|||
|
|
import threading
|
|||
|
|
import numpy as np
|
|||
|
|
import cv2
|
|||
|
|
from concurrent.futures import ThreadPoolExecutor
|
|||
|
|
|
|||
|
|
SOCK_PATH = "/tmp/edge-infer.sock"
|
|||
|
|
|
|||
|
|
def make_test_image(w=640, h=640):
|
|||
|
|
"""生成随机测试图片"""
|
|||
|
|
img = np.random.randint(0, 255, (h, w, 3), dtype=np.uint8)
|
|||
|
|
_, buf = cv2.imencode('.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 90])
|
|||
|
|
return buf.tobytes()
|
|||
|
|
|
|||
|
|
def infer_one(jpeg_bytes):
|
|||
|
|
"""发送一次推理请求,返回响应时间(ms)"""
|
|||
|
|
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|||
|
|
sock.connect(SOCK_PATH)
|
|||
|
|
|
|||
|
|
msg = {
|
|||
|
|
"stream_id": 0,
|
|||
|
|
"device_id": "bench",
|
|||
|
|
"url": "bench://test",
|
|||
|
|
"ts": 1234567890.0,
|
|||
|
|
"jpeg_b64": base64.b64encode(jpeg_bytes).decode("utf-8")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
data = json.dumps(msg).encode("utf-8")
|
|||
|
|
|
|||
|
|
t0 = time.time()
|
|||
|
|
sock.sendall(struct.pack(">I", len(data)) + data)
|
|||
|
|
hdr = sock.recv(4)
|
|||
|
|
length = struct.unpack(">I", hdr)[0]
|
|||
|
|
result = json.loads(sock.recv(length).decode("utf-8"))
|
|||
|
|
elapsed = (time.time() - t0) * 1000 # ms
|
|||
|
|
|
|||
|
|
sock.close()
|
|||
|
|
return elapsed, result
|
|||
|
|
|
|||
|
|
def test_single_infer():
|
|||
|
|
"""单次推理延迟"""
|
|||
|
|
print("\n=== 单次推理延迟测试 ===")
|
|||
|
|
jpeg = make_test_image()
|
|||
|
|
times = []
|
|||
|
|
for i in range(5):
|
|||
|
|
t, _ = infer_one(jpeg)
|
|||
|
|
times.append(t)
|
|||
|
|
print(f" 第 {i+1} 次: {t:.1f} ms")
|
|||
|
|
|
|||
|
|
# 跳过第一次预热
|
|||
|
|
times = times[1:]
|
|||
|
|
avg = sum(times) / len(times)
|
|||
|
|
print(f"\n 平均延迟 (排除预热): {avg:.1f} ms")
|
|||
|
|
print(f" 理论 FPS: {1000/avg:.1f}")
|
|||
|
|
return avg
|
|||
|
|
|
|||
|
|
def test_throughput():
|
|||
|
|
"""连续推理吞吐"""
|
|||
|
|
print("\n=== 连续推理吞吐测试 ===")
|
|||
|
|
jpeg = make_test_image()
|
|||
|
|
count = 30
|
|||
|
|
t0 = time.time()
|
|||
|
|
|
|||
|
|
for i in range(count):
|
|||
|
|
_, _ = infer_one(jpeg)
|
|||
|
|
if (i+1) % 10 == 0:
|
|||
|
|
elapsed = time.time() - t0
|
|||
|
|
print(f" {i+1}/{count} 完成, 累计: {elapsed:.1f}s, FPS: {(i+1)/elapsed:.1f}")
|
|||
|
|
|
|||
|
|
total = time.time() - t0
|
|||
|
|
fps = count / total
|
|||
|
|
print(f"\n 总计 {count} 帧: {total:.2f}s")
|
|||
|
|
print(f" 吞吐: {fps:.1f} FPS")
|
|||
|
|
print(f" 每帧延迟: {total/count*1000:.1f} ms")
|
|||
|
|
return fps
|
|||
|
|
|
|||
|
|
def test_concurrent(workers=4):
|
|||
|
|
"""多 worker 并发推理"""
|
|||
|
|
print(f"\n=== {workers} 路并发测试 ===")
|
|||
|
|
jpeg = make_test_image()
|
|||
|
|
results_per_worker = []
|
|||
|
|
|
|||
|
|
def worker_task(worker_id, n_frames=20):
|
|||
|
|
times = []
|
|||
|
|
for _ in range(n_frames):
|
|||
|
|
t, _ = infer_one(jpeg)
|
|||
|
|
times.append(t)
|
|||
|
|
return worker_id, times
|
|||
|
|
|
|||
|
|
t0 = time.time()
|
|||
|
|
with ThreadPoolExecutor(max_workers=workers) as pool:
|
|||
|
|
futures = [pool.submit(worker_task, i, 15) for i in range(workers)]
|
|||
|
|
for f in futures:
|
|||
|
|
wid, times = f.result()
|
|||
|
|
results_per_worker.append((wid, sum(times)/len(times), max(times), min(times)))
|
|||
|
|
|
|||
|
|
total = time.time() - t0
|
|||
|
|
total_frames = workers * 15
|
|||
|
|
total_fps = total_frames / total
|
|||
|
|
|
|||
|
|
for wid, avg, mx, mn in results_per_worker:
|
|||
|
|
print(f" Worker {wid}: avg={avg:.1f}ms, max={mx:.1f}ms, min={mn:.1f}ms")
|
|||
|
|
|
|||
|
|
print(f"\n 并发 {workers} 路: 总计 {total_frames} 帧, {total:.2f}s")
|
|||
|
|
print(f" 总吞吐: {total_fps:.1f} FPS")
|
|||
|
|
print(f" 单路等效 FPS: {total_fps/workers:.1f}")
|
|||
|
|
return total_fps
|
|||
|
|
|
|||
|
|
def test_power():
|
|||
|
|
"""读取 NPU 功耗信息"""
|
|||
|
|
import subprocess
|
|||
|
|
try:
|
|||
|
|
result = subprocess.run(['npu-smi', 'info'], capture_output=True, text=True, timeout=5)
|
|||
|
|
print("\n=== NPU 状态 ===")
|
|||
|
|
print(result.stdout)
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"无法读取 NPU 状态: {e}")
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
print("=" * 60)
|
|||
|
|
print(" 昇腾 310B4 + ACL 原生推理 性能基准测试")
|
|||
|
|
print("=" * 60)
|
|||
|
|
|
|||
|
|
test_power()
|
|||
|
|
test_single_infer()
|
|||
|
|
test_throughput()
|
|||
|
|
test_concurrent(2)
|
|||
|
|
test_concurrent(4)
|
|||
|
|
test_concurrent(6)
|
|||
|
|
|
|||
|
|
print("\n" + "=" * 60)
|
|||
|
|
print("测试完成")
|
|||
|
|
print("=" * 60)
|