Files
ocr_data/tools/throughput_jsonl.py
2026-09-28 16:09:31 +08:00

160 lines
6.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
分析 数据.jsonl 的处理吞吐:单台机器一天能处理多少小时的视频数据。
两种口径:
A) 墙钟吞吐(真实):用 process_start/process_end 求整批的实际墙钟跨度,
总视频时长 ÷ 墙钟跨度 = 机器真实吞吐(含当时的并发/排队效果)。
B) 单视频平均(理想并发):按每个视频 duration/elapsed 的实时倍率,
再乘并发数估算(假设无 OCR/CPU 排队,即"OCR 速度够"的上限)。
用法: python throughput_jsonl.py [数据.jsonl路径] [并发数]
默认: python throughput_jsonl.py 数据.jsonl 1
"""
import json
import sys
from datetime import datetime
JSONL_PATH = sys.argv[1] if len(sys.argv) > 1 else "数据.jsonl"
WORKERS = int(sys.argv[2]) if len(sys.argv) > 2 else 1
OCR_QPS_PER_MACHINE = float(sys.argv[3]) if len(sys.argv) > 3 else 14.0 # 单台 OCR 的 QPS
def parse_dt(s):
if not s:
return None
try:
return datetime.fromisoformat(s)
except (ValueError, TypeError):
return None
def fmt_h(hours):
return f"{hours:.1f} 小时"
def main():
try:
f = open(JSONL_PATH, encoding="utf-8")
except FileNotFoundError:
print(f"找不到文件: {JSONL_PATH}")
return
n = 0
total_dur = 0.0 # 全部视频时长(秒)
total_elapsed = 0.0 # 全部单视频处理耗时(秒),含并发
starts, ends = [], []
per_video_ratio = [] # 每视频 duration/elapsed
stage_sum = {} # 各阶段耗时累计
with_timing = 0
total_ocr = 0 # 全部 OCR 请求次数累计
with_ocr = 0
with f:
for line in f:
line = line.strip()
if not line:
continue
try:
rec = json.loads(line)
except json.JSONDecodeError:
continue
n += 1
dur = rec.get("duration") or 0
try:
dur = float(dur)
except (TypeError, ValueError):
dur = 0.0
total_dur += dur
el = rec.get("elapsed_seconds")
if el:
total_elapsed += float(el)
if dur > 0:
per_video_ratio.append(dur / float(el))
st = parse_dt(rec.get("process_start"))
en = parse_dt(rec.get("process_end"))
if st:
starts.append(st)
if en:
ends.append(en)
sd = rec.get("stage_durations") or {}
if sd:
with_timing += 1
for k, v in sd.items():
try:
stage_sum[k] = stage_sum.get(k, 0.0) + float(v)
except (TypeError, ValueError):
pass
ic = rec.get("image_counts") or {}
toc = ic.get("total_ocr_count")
if toc:
try:
total_ocr += int(toc)
with_ocr += 1
except (TypeError, ValueError):
pass
print(f"文件: {JSONL_PATH}")
print("=" * 52)
print(f"记录数: {n}")
print(f"视频总时长: {total_dur/3600:.1f} 小时 ({total_dur:.0f} 秒)")
print(f"单视频耗时累加: {total_elapsed/3600:.1f} 小时 ({total_elapsed:.0f} 秒)")
if with_ocr:
print(f"OCR 请求总数: {total_ocr} (平均 {total_ocr/with_ocr:.0f} 次/视频, {with_ocr} 条有统计)")
# ---- 口径A:真实墙钟吞吐 ----
print("\n[A] 真实墙钟吞吐(按 process_start/end 的整批实际跨度)")
if starts and ends:
span = (max(ends) - min(starts)).total_seconds()
if span > 0:
tp = total_dur / span # 视频秒/墙钟秒
print(f" 整批墙钟跨度: {span/3600:.2f} 小时")
print(f" 实时倍率: {tp:.2f}x (每 1 秒墙钟处理 {tp:.2f} 秒视频)")
print(f" → 一天可处理: {fmt_h(tp*24)} 的视频")
vids_per_day = n / span * 86400 if span else 0
print(f" → 一天可处理: {vids_per_day:.0f} 个视频")
# ---- OCR 需求:这台机器实际产生多少 OCR/秒、能喂几台 OCR ----
if total_ocr:
ocr_per_sec = total_ocr / span
machines_fed = ocr_per_sec / OCR_QPS_PER_MACHINE
print(f"\n [OCR 配比](单台 OCR 按 {OCR_QPS_PER_MACHINE:.0f} QPS 计)")
print(f" 本机 OCR 速率: {ocr_per_sec:.2f} OCR/秒")
print(f" → 本机能喂满: {machines_fed:.2f} 台 OCR")
if machines_fed > 0:
print(f" → 一台 OCR 可喂: {1/machines_fed:.1f} 台这样的处理机")
else:
print(" 墙钟跨度为0,无法计算")
else:
print(" 缺少 process_start/process_end 字段,跳过")
# ---- 口径B:按单视频平均实时倍率 × 并发 ----
print(f"\n[B] 理想吞吐(单视频平均倍率 × 并发{WORKERS},假设无排队=OCR够快)")
if per_video_ratio:
avg_ratio = sum(per_video_ratio) / len(per_video_ratio)
# 也给一个总量口径:总视频时长/总耗时(更稳,受长短视频加权)
weighted = total_dur / total_elapsed if total_elapsed else 0
print(f" 单视频平均倍率: {avg_ratio:.2f}x")
print(f" 加权倍率(总/总):{weighted:.2f}x")
print(f" → 单并发一天: {fmt_h(weighted*24)} 的视频")
print(f" → {WORKERS}并发一天: {fmt_h(weighted*24*WORKERS)} 的视频(理想线性,实际受CPU限制)")
else:
print(" 缺少 elapsed_seconds 字段,跳过")
# ---- 各阶段占比 ----
if stage_sum:
print(f"\n[阶段耗时占比]({with_timing} 条有计时)")
main_t = stage_sum.get("main", sum(v for k, v in stage_sum.items() if k != "main"))
for k in ["split_frame", "merge", "getsrt"]:
if k in stage_sum:
pct = stage_sum[k] / main_t * 100 if main_t else 0
print(f" {k:12s}: {stage_sum[k]/3600:6.1f} 小时 ({pct:.0f}%)")
if __name__ == "__main__":
main()