You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jupyter中%%time为何无法正确统计%%sh命令的CPU时间?

现象说明

这个不是程序bug,属于%%time魔法和%%sh魔法配合使用的已知机制限制:

  • %%time默认的CPU时间统计范围是IPython主进程自身的资源消耗,不会自动递归统计主进程拉起的所有子进程的CPU占用。
  • %%sh的执行逻辑是:IPython主进程仅负责创建shell子进程、传入脚本内容、等待子进程执行完成后回收输出,子进程运行计算任务的整个周期里,主进程基本处于IO等待状态,只会产生极少量的进程调度、管道读写开销,这就是观测到CPU时间只有十几毫秒的原因——这部分统计的就是主进程自身的真实消耗,数值是准确的。
  • 输出里的Wall time(墙钟时间)始终是准确的,因为这个指标统计的是单元格从开始执行到完全结束的真实流逝时间,不管耗时发生在主进程还是子进程都会被计入。

%time官方文档里标注的“统计Python语句或表达式执行时间”的描述完全准确:%%sh作为Python层面调用的魔法命令,它本身在主进程里的执行耗时确实只有十几毫秒,子进程内部运行的计算逻辑不属于IPython主进程上下文里的Python语句,自然不会被默认的CPU统计逻辑覆盖。

便捷统计方案

不用给每一行shell命令单独加time前缀,两种方案都能用,看自己习惯:

  • 轻量无配置方案:写%%sh块时,在脚本最开头加一行时间格式定义,后续所有多行shell逻辑正常写即可,输出格式和原生%%time基本对齐:
%%sh
TIMEFORMAT=$'CPU times: user %U s, sys %S s, total %P\nWall time: %R s'
time bash -c '
# 下方直接写原有多行shell脚本,不需要做任何修改
python -c "for i in range(10000000): a = i*i"
echo "其他任意shell命令都可以放这里"
'
  • 一劳永逸方案:在Notebook最开头运行一次自定义魔法代码,后续直接用%%timesh替代原来的%%time + %%sh组合即可,输出格式和原生%%time完全一致,还会自动累加所有子进程的CPU消耗,不需要修改原有shell脚本内容:
from IPython.core.magic import register_cell_magic
import resource
import time

@register_cell_magic
def timesh(line, cell):
    start_wall = time.perf_counter()
    # 记录主进程、已结束子进程的初始CPU时间
    start_self = resource.getrusage(resource.RUSAGE_SELF)
    start_child = resource.getrusage(resource.RUSAGE_CHILDREN)
    # 执行sh块内容
    get_ipython().run_cell_magic("sh", line, cell)
    # 计算时间差值
    end_wall = time.perf_counter()
    end_self = resource.getrusage(resource.RUSAGE_SELF)
    end_child = resource.getrusage(resource.RUSAGE_CHILDREN)
    
    user_ms = ((end_self.ru_utime - start_self.ru_utime) + (end_child.ru_utime - start_child.ru_utime)) * 1000
    sys_ms = ((end_self.ru_stime - start_self.ru_stime) + (end_child.ru_stime - start_child.ru_stime)) * 1000
    total_ms = user_ms + sys_ms
    wall_ms = (end_wall - start_wall) * 1000
    
    print(f"CPU times: user {user_ms:.0f} ms, sys: {sys_ms:.0f} ms, total: {total_ms:.0f} ms")
    print(f"Wall time: {wall_ms:.0f} ms")

使用示例:

%%timesh
python -c "for i in range(10000000): a = i*i"
echo "任意多行shell命令都可以直接写"

注:上述自定义魔法基于POSIX系统的resource模块实现,Windows环境下可以用psutil库做类似的进程统计,核心逻辑一致。

内容的提问来源于stack exchange,提问作者Graham501617

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 18:36:20