Set Max_split_size_mb To Avoid Oom In Pytorch - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NVIDIA CUDA
PyCUDA
Caffe
GopherDaily
V2EX    CUDA

Set Max_split_size_mb To Avoid Oom In Pytorch

  •  
  •   GopherDaily 2023-07-26 12:25:01 +08:00 2737 次点击
    这是一个创建于 873 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我们在 k8s 中部署了 stable-diffusion-webui 供任何想要体验的 Stable Diffusion Model 的用户使用. 随着一个又一个的请求, 我们频繁的遇到 CUDA 的 OOM 错误. 其中的一小部分确实是因为用户请求需要的资源超过了对应 GPU 能够提供的内存.

    剩下的, 占大部分的, 是类似如下的令人困惑的场景.

    {"error": "OutOfMemoryError", "detail": "", "body": "", "errors": "CUDA out of memory. Tried to allocate 1024.00 MiB (GPU 0; 11.76 GiB total capacity; 7.92 GiB already allocated; 784.31 MiB free; 10.63 GiB reserved in total by PyTorch) If reserved memory is >> allocated memory try setting max_split_size_mb to avoid fragmentation. See documentation for Memory Management and PYTORCH_CUDA_ALLOC_CONF"} 

    根据对 memory_stats 的理解:

    • GPU 的内存是 11.76G
    • pytorch 已经从 GPU 出请求的内存是 10.63G
    • pytorch 已经分配给用户的内存是 7.92G
    • pytorch 还可以分配的内存为 784.31M, 远小于 reserved 减 allocated 的 2.71G

    这部分内存去哪儿了呢? 为什么在用户申请的时候依然没有被回收呢?

    pytorch 是如何分配内存的?.

    当用户请求内存时, pytorch 的处理流程可以简化为:

    1. 尝试通过 get_free_block 去寻找满足要求的空闲 Block
    2. 如果失败, 则通过 trigger_free_memory_callbacks 去回收已分配但不再使用的 Block 后, 再次尝试 get_free_block
    3. 如果失败, 则通过 alloc_block 去向 GPU 申请新的 Block
    4. 如果失败, 则通过 release_available_cached_blocks 将已申请但未分配的 Block 释放后再次尝试 alloc_block
    5. 如果失败, 则通过 release_cached_blocks 将所有已申请但未分配的 Block 释放, 再次尝试 alloc_block

    我们注意到 pytorch 向 GPU 申请和分配给用户的内存都以 Block 为单位. pytorch 向 GPU 申请的 Block 大小并不固定, 受当时用户请求内存大小的影响. 用户释放内存后, Block 返回给 pytorch 并成为空闲状态. 用户下次申请时优先会复用空闲 Block, 而不是直接向 GPU 申请.

    如果用户申请的内存大小小于满足要求的空闲 Block, pytorch 会进行一次 split 操作. 将 Block 分割成两个 Block, 除去用户请求大小的内存会被分割成一个独立的 Block, 留待后用并通过双向链表和分配给用户的 Block 相关联.

    trigger_free_memory_callbacks 的回收过程会将相邻的空闲 Block 合并, 提高后续分配的灵活性.

    相较于其他内存管理机制, pytorch 的内存管理相对简略:

    • pytorch 回收 Block, 只尝试合并相邻的空闲的 Block, 并不会进行搬运操作来处理不相连的空闲 Block
    • 一旦 Block 被分割, 则 pytorch 无法将其释放. cudaMalloc 和 cudaFree 是对称的, 你无法仅释放某次分配的一部分内存.

    上述的两点, 造成了 pytorch 可能因为 Block 碎片化, 导致大量内存无法被使用.

    假设在某次分配内存时, pytorch 根据用户请求向 GPU 申请了一个 256M 的 Block.
    <-------------------------- 256M ----------------------------->

    经过多次分配和回收, 其使用情况可能变成如下.
    <-- 28M(allocated) --><-- 100M(free) --><-- 28M(allocated) --><-- 100M(free) -->

    此时如果用户申请 160M 内存:

    • 虽然空闲的总内存大于 160M, 但是因为没有大于 160M 的 Block, 所以无法分配
    • pytorch 也无法将空闲的 100M+100M 内存返回给 GPU, 导致也无法向 GPU 申请 160M 内存.

    max_split_size_mb 的作用

    max_split_size_mb 的作用在于禁止 pytorch 对任何大于该大小的 Block 进行分割操作, 从而控制碎片化的程度. 我们上文讲诉的都是在未主动设置 max_split_size_mb 的情况下的逻辑, 此时 max_split_size_mb 取默认值 MAX_INT.

    我们并没有找到官方推荐的 max_split_size_mb, 我们也不熟悉 pytorch 和 nvida, 很难给出一个很好的推荐值. 从实际使用来和直观逻辑来说, 128/256/512 之类的值都是可选的, 切实的避免了 OOM, 也没有导致明显的性能负担.

    garbage_collection_threshold

    pytorch 默认仅在无法获取到合适的空闲 Block 时触发回收, 这个值可以控制当 allocated/capacity 超过此值时触发主动的回收.

    Expandable Segments

    pytorch 最新(>v2.0.1)的 master 分支中添加了 Expandable Segments, 可能也可以缓解碎片化的问题.

    References

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     886 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 44ms UTC 19:51 PVG 03:51 LAX 11:51 JFK 14:51
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86