批量解析跑到一半超时退出,先别急着把并发调低或者把超时时间调大。这两个动作对应的病因完全不同:并发数影响的是多个文件同时抢算力和显存,超时时间影响的是单个任务允许多久被砍。更常见的实际情况是,一批文件里有几个页数特别多的长尾文件拖过了超时线,其余文件其实跑得很快。所以判断顺序通常是:先从日志里找出慢文件,再单独跑一次,最后才动并发和超时。
先按文件把批量任务拆开看:从日志里取单文件耗时,按页数归一后排个序。如果慢的集中在少数大文件,问题在单文件页数与显存占用,优先拆分和重试;如果所有文件都慢、且并发调高后才开始失败,问题在并发档位。单跑一个最慢文件,超时则调单文件参数,不超时则回去做并发梯度测试。所有阈值以本机实际观测和项目文档为准。
从日志里取出单文件耗时,找出最慢的几个文件
先确认 TeleOCR 的解析日志里有没有单文件级别的完成记录,通常字段是文件名、页数、耗时毫秒、状态。如果日志只打了批次总耗时,就把日志级别调到能看到单文件粒度,或者在脚本里对每个文件自己计时。
取耗时并排序的骨架(字段名按实际日志调整):
grep 'parse done' teleocr.log \
| sed -n 's/.*file=\([^ ]*\).*pages=\([0-9]*\).*cost_ms=\([0-9]*\).*/\1 \2 \3/p' \
| awk '{printf "%s\t%s页\t%s ms\t%.1f ms/页\n", $1, $2, $3, $3/$2}' \
| sort -k4 -nr | head -20
关键是最后一列的「每页耗时」。大文件天然比小文件慢,如果只按总耗时排序,会把正常的大文件误判成异常。按页数归一后再比,才能看出某个文件是不是真的异常。另外顺手统计一下超时文件占总文件数的比例:如果只有一两个文件超时,那是长尾问题;如果大部分文件都在同一个时间点附近被砍,更像并发或超时配置问题。这一步的判断依据是日志和排序结果,不需要改任何配置。
把最慢的那个文件单独跑一次,排除并发干扰
从上一节排出来的前几名里挑一个,用单文件模式跑,并发设为 1,让这个任务独占资源。
# 命令名与参数以项目文档为准,下面是通用骨架
/usr/bin/time -v teleocr parse \
`--input` ./samples/慢文件.pdf \
`--output` ./out_single \
`--concurrency` 1 \
`--timeout` 600
记录实际耗时:看 /usr/bin/time -v 输出的 real 时间,或者解析完成后日志里的 cost_ms,两者取一个固定口径,后面所有对比都用同一个。
两种结果对应两个方向。单跑正常、批量才超时,说明单文件本身没问题,是并发抢占资源导致单个任务变慢,接下来去查并发档位。单跑也超时,说明这个文件自己就压不进超时窗口,要么页数太多,要么某几页触发了异常处理,接下来要么调单文件参数,要么把它拆开。注意单跑时也要观察显存和内存峰值,页数多的 PDF 在渲染阶段占用会明显上升。
检查当前的并发数与超时时间配置
很多批量脚本只暴露了并发数,超时时间用的是默认值,或者反过来。两个参数要放在一起看:并发调高,多个文件共享同一张卡或同一批 CPU,单个文件的实际耗时会被拉长,原本不超时的文件也可能踩线;只调超时时间,慢任务不会被砍了,但队列会越排越长,整批任务的完成时间没有改善。
通用配置骨架,参数名以项目文档为准:
parse:
concurrency: 2 # 同时解析的任务数,注意确认是「文件级」还是「页级」
timeout_sec: 600 # 单任务超时,注意确认是单文件还是单页
max_retry: 1
retry_delay_sec: 5
先确认两个语义:concurrency 指的是同时几个文件,还是同一文件里同时几页;timeout 是单文件超时还是单页超时。这两个语义不同,调参方向会反过来。确认方式看项目文档或直接看代码里取参数的位置,不要靠猜。
做一组并发梯度测试,记录耗时与显存变化
固定同一批输入文件,最好包含上一节里最慢的那几个,然后从低并发往高跑。每档只改 concurrency,超时时间先放宽到不会误砍的值,避免结果被超时截断。
for c in 1 2 4 6; do
/usr/bin/time -v teleocr batch `--input` ./list.txt \
`--concurrency` $c `--timeout` 1800 2> run_c${c}.time
nvidia-smi `--query-gpu`=memory.used `--format`=csv,noheader -l 1 > mem_c${c}.csv &
# 采样进程在批处理结束后手动停掉
done
每档至少记三个指标:整批总耗时、峰值显存(从采样的 csv 里取最大值,同时看一眼系统内存)、失败或超时文件数。判断方法不是找总耗时最低的那档,而是找「不再出现失败文件、且总耗时不再明显下降」的档位,把它作为本机稳定档。显存随并发接近上限时,即使这一档还没报错,也要往回退一档留余量。这组测试的结果只对本机硬件和这批文件有效,换机器或换文档类型要重新跑。
把长文件拆分或排队重试,避免一个文件拖垮整批
梯度测试确定了稳定并发后,剩下的长尾文件单独处理。按页数分组是成本最低的做法:把文件按页数分成小文件组和大文件组,小文件组用稳定并发跑,大文件组降到 1 到 2 并发、超时放宽,两组分别记录各自的结果。
如果工具支持页码范围,大文件可以直接切页分批解析,最后按页序合并结果;如果不支持,就用重试把失败文件捞回来,而不是让整批任务因为一个文件中断。
#!/usr/bin/env bash
# 失败文件重试骨架,超时和重试次数按实际情况调整
: > failed.txt
while read -r f; do
if ! teleocr parse `--input` "$f" `--timeout` 900 `--concurrency` 1; then
echo "$f" >> failed.txt
fi
done < list.txt
# 对失败文件再跑一轮
if [ -s failed.txt ]; then
while read -r f; do
teleocr parse `--input` "$f" `--timeout` 900 `--concurrency` 1 || echo "$f" >> failed_again.txt
done < failed.txt
fi
重试结束后必须核对完整性,不能只看退出码。核对三件事:输入文件数是否等于输出结果数;failed_again.txt 里的文件是否已经人工确认过或换方式处理;大文件抽查几份,比对原始 PDF 的页数和输出结果里的页数是否一致,避免重试时只解析了部分页面却当成成功。这套做法只是让批量任务在出现长尾文件时仍能整体完成,并不会让单文件解析变快。