pytorch 训练_基于文件存储UFS的Pytorch训练IO优化实践

2023-11-04

我们在协助某AI客户排查一个UFS文件存储的性能case时发现,其使用的Pytorch训练IO性能和硬件的IO能力有很大的差距(后面内容有具体性能对比数据)。

让我们感到困惑的是: UFS文件存储,我们使用fio自测可以达到单实例最低10Gbps带宽、IOPS也可达到2w以上。该AI客户在高IOPS要求的AI单机小模型训练场景下,或者之前使用MXNet、TensorFlow框架时,IO都能跑到UFS理论性能,甚至在大型分布式训练场景中,UFS也可以完全胜任。

于是我们开启了和客户的一次深度联合排查。

初步尝试优化

一、调整参数:

基于上述情况,首先考虑是不是使用Pytorch的姿势不对?参考网上提到经验,客户调整batch_size、Dataloader等参数。

Batch_size

默认batch_size为256,根据内存和显存配置尝试更改batch_size大小,让一次读取数据更多,发现实际对效率没有提升。通过分析是由于batch_size设置与数据读取逻辑没有直接关系,IO始终会保留单队列与后端交互,不会降低网络交互上的整体延时(因为用的是UFS文件存储,后面会讲到为什么用)。

Pytorch Dataloader

Pytorch框架dataloader的worker负责数据的读取和加载、分配。通过batch_sampler将batch数据分配给对应的worker,由worker从磁盘读取数据并加载数据到内存,dataloader从内存中读取相应batch做迭代训练。这里尝试调整了worker_num参数为CPU核数或倍数,发现提升有限,反而内存和CPU的开销提升了不少,整体加重了训练设备的负担,通过 worker加载数据时的网络开销并不会降低,与本地SSD盘差距依然存在。

这个也不难理解,后面用strace排查的时候,看到CPU更多的时候在等待。

所以:从目前信息来看,调整Pytorch框架参数对性能几乎没有影响。

二、尝试不同存储产品

在客户调整参数的同时,我们也使用了三种存储做验证,来看这里是否存在性能差异、差异到底有多大。在三种存储产品上放上同样的数据集:

  1. 单张平均大小20KB的小图片,总量2w张。
  2. 以目录树方式存到三种存储下的相同路径,使用Pytorch常用的标准读图接口CV2和PIL

测试结果,如下图:

注:SSHFS基于X86物理机(32核/64G/480G SSD*6 raid10)搭建,网络25Gbps

结论:通过对存储性能实测, UFS文件存储较本地盘、单机SSHFS性能差距较大。

为什么会选用这两种存储(SSHFS和本地SSD)做UFS性能对比?

当前主流存储产品的选型上分为两类:自建SSHFS/NFS或采用第三方NAS服务(类似UFS产品),个别场景中也会将需要的数据下载到本地SSD盘做训练。传统SSD本地盘拥有极低的IO延时,一个IO请求处理基本会在us级别完成,针对越小的文件,IO性能越明显。受限于单台物理机配置,无法扩容,数据基本 “即用即弃”。而数据是否安全也只能依赖磁盘的稳定性,一旦发生故障,数据恢复难度大。但是鉴于本地盘的优势,一般也会用作一些较小模型的训练,单次训练任务在较短时间即可完成,即使硬件故障或者数据丢失导致训练中断,对业务影响通常较小。

用户通常会使用SSD物理机自建SSHFS/NFS共享文件存储,数据IO会通过以太网络,较本地盘网络上的开销从us级到ms级,但基本可以满足大部分业务需求。但用户需要在日常使用中同时维护硬件和软件的稳定性,并且单台物理机有存储上限,如果部署多节点或分布式文件系统也会导致更大运维精力投入。

我们把前面结论放到一起看:

  1. 隐形结论:Tensorflow、Mxnet框架无问题。
  2. 调整Pytorch框架参数对性能几乎没有影响。

3、Pytorch+UFS的场景下, UFS文件存储较本地SSD盘、单机SSHFS性能差距大。

结合以上几点信息并与用户确认后的明确结论:

UFS结合非Pytorch框架使用没有性能瓶颈, Pytorch框架下用本地SSD盘没有性能瓶颈,用SSHFS性能可接受。 那原因就很明显了,就是Pytorch+UFS文件存储这个组合存在IO性能问题。

深入排查优化

看到这里,大家可能会有个疑问:是不是不用UFS,用本地盘就解决了?

答案是不行,原因是训练所需的数据总量很大,很容易超过了单机的物理介质容量,另外也出于数据安全考虑,存放单机有丢失风险,而UFS是三副本的分布式存储系统,并且UFS可以提供更弹性的IO性能。

根据以上的信息快速排查3个结论,基本上可以判断出: Pytorch在读UFS数据过程中,文件读取逻辑或者UFS存储IO耗时导致。于是我们通过strace观察Pytorch读取数据整体流程:

通过strace发现,CV2方式读取UFS里的文件(NFSV4协议)有很多次SEEK动作,即便是单个小文件的读取也会“分片”读取,从而导致了多次不必要的IO读取动作,而最耗时的则是网络,从而导致整体耗时成倍增长。这也是符合我们的猜测。

简单介绍一下NFS协议特点:

NAS所有的IO都需要经过以太网,一般局域网内延时在1ms以内。以NFS数据交互为例,通过图中可以看出,针对一次完整的小文件IO操作将涉及元数据查询、数据传输等至少5次网络交互,每次交互都会涉及到client与server集群的一个TTL,其实这样的交互逻辑会存在一个问题,当单文件越小、数量越大时则延时问题将越明显,IO过程中有过多的时间消耗在网络交互,这也是NAS类存储在小文件场景下面临的经典问题。

对于UFS的架构而言,为了达到更高扩展性、更便利的维护性、更高的容灾能力,采用接入层、索引层和数据层的分层架构模式,一次IO请求会先经过接入层做负载均衡,client端再访问后端UFS索引层获取到具体文件信息,最后访问数据层获取实际文件,对于KB级别的小文件,实际在网络上的耗时比单机版NFS/SSHFS会更高。

从Pytorch框架下两种读图接口来看:CV2读取文件会“分片”进行,而PIL虽然不会“分片”读取,但是基于UFS分布式架构,一次IO会经过接入、索引、数据层,网络耗时也占比很高。我们存储同事也实际测试过这2种方法的性能差异:通过strace发现,相比OpenCV的方式,PIL的数据读取逻辑效率相对高一些。

优化方向一: 如何降低与UFS交互频次,从而降低整体存储网络延时

CV2:对单个文件而言,“分片读取”变“一次读取”

通过对Pytorch框架接口和模块的调研,如果使用 OpenCV方式读取文件可以用2个方法, cv2.imread和cv2.imdecode。

默认一般会用cv2.imread方式,读取一个文件时会产生9次lseek和11次read,而对于图片小文件来说多次lseek和read是没有必要的。cv2.imdecode可以解决这个问题,它通过一次性将数据加载进内存,后续的图片操作需要的IO转化为内存访问即可。

两者的在系统调用上的对比如下图:

我们通过使用cv2.imdecode方式替换客户默认使用的cv2.imread方式,单个文件的总操作耗时从12ms下降到6ms。但是内存无法cache住过大的数据集,不具备任意规模数据集下的训练,但是整体读取性能还是提升明显。使用cv2版本的benchmark对一个小数据集进行加载测试后的各场景耗时如下(延迟的非线性下降是因为其中包含GPU计算时间):

PIL:优化dataloader元数据性能,缓存文件句柄

通过PIL方式读取单张图片的方式,Pytorch处理的平均延迟为7ms(不含IO时间),单张图片读取(含IO和元数据耗时)平均延迟为5-6ms,此性能水平还有优化空间。

由于训练过程会进行很多个epoch的迭代,而每次迭代都会进行数据的读取,这部分操作从多次训练任务上来看是重复的,如果在训练时由本地内存做一些缓存策略,对性能应该有提升。但直接缓存数据在集群规模上升之后肯定是不现实的,我们初步只缓存各个训练文件的句柄信息,以降低元数据访问开销。

我们修改了Pytorch的dataloader实现,通过本地内存cache住训练需要使用的文件句柄,可以避免每次都尝试做open操作。测试后发现1w张图片通过100次迭代训练后发现,单次迭代的耗时已经基本和本地SSD持平。但是当数据集过大,内存同样无法cache住所有元数据,所以使用场景相对有限,依然不具备在大规模数据集下的训练伸缩性。

UFS server端元数据预加载

以上client端的优化效果比较明显,但是客户业务侧需要更改少量训练代码,最主要是client端无法满足较大数据量的缓存,应用场景有限,我们继续从server端优化,尽量降低整个链路上的交互频次。

正常IO请求通过负载均衡到达索引层时,会先经过索引接入server,然后到索引数据server。考虑到训练场景具有目录访问的空间局部性,我们决定增强元数据预取的功能。通过客户请求的文件,引入该文件及相应目录下所有文件的元数据,并预取到索引接入server,后续的请求将命中缓存,从而减少与索引数据server的交互,在IO请求到达索引层的第一步即可获取到对应元数据,从而降低从索引数据server进行查询的开销。

经过这次优化之后,元数据操作的延迟较最初能够下降一倍以上,在客户端不做更改的情况下,读取小文件性能已达到本地SSD盘的50%。看来单单优化server端还是无法满足预期,通过执行Pytorch的benchmark程序,我们得到UFS和本地SSD盘在整个数据读取耗时。

此时很容易想到一个问题: 非Pytorch框架在使用UFS做训练集存储时,为什么使用中没有遇到IO性能瓶颈?

通过调研其他框架的逻辑发现:无论是MXNet的rec文件,Caffe的LMDB,还是TensorFlow的npy文件,都是在训练前将大量图片小文件转化为特定的数据集格式,所以使用UFS在存储网络交互更少,相对Pytorch直接读取目录小文件的方式,避免了大部分网络上的耗时。这个区别在优化时给了我们很大的启示,将目录树级别小文件转化成一个特定的数据集存储,在读取数据做训练时将IO发挥出最大性能优势。

优化方向二:目录级内的小文件转换为数据集,最大程度降到IO网络耗时

基于其他训练框架数据集的共性功能,我们UFS存储团队赶紧开工,几天开发了针对Pytorch框架下的数据集转换工具,将小文件数据集转化为UFS大文件数据集并对各个小文件信息建立索引记录到index文件,通过index文件中索引偏移量可随机读取文件,而整个index文件在训练任务启动时一次性加载到本地内存,这样就将大量小文件场景下的频繁访问元数据的开销完全去除了,只剩下数据IO的开销。该工具后续也可直接应用于其他AI类客户的训练业务。

工具的使用很简单,只涉及到两步:

  • 使用UFS自研工具将Pytorch数据集以目录形式存储的小文件转化为一个大文件存储到UFS上,生成date.ufs和index.ufs。
  • 使用我方提供Folder类替换pytorch原有代码中的torchvision.datasets.ImageFolder数据加载模块(即替换数据集读取方法),从而使用UFS上的大文件进行文件的随机读取。只需更改3行代码即可。

20行:新增from my_dataloader import *

205行:train_dataset = datasets.ImageFolder改为train_dataset = MyImageFolder

224行:datasets.ImageFolder改为MyImageFolder

通过github上Pytorch测试demo对imagenet数据集进行5、10、20小时模拟训练,分别读取不同存储中的数据,具体看下IO对整体训练速度的影响。(数据单位:完成的epoch的个数)

测试条件:

GPU服务器:P40*4物理机,48核256G,数据盘800G*6 SATA SSD RAID10

SSHFS:X86物理机32核/64G,数据盘480G*6 SATA SSD RAID10

Demo: https:// github.com/pytorch/exam ples/tree/master/imagenet

数据集:总大小148GB、图片文件数量120w以上

通过实际结果可以看出: UFS数据集方式效率已经达到甚至超过本地SSD磁盘的效果。而UFS数据集转化方式,客户端内存中只有少量目录结构元数据缓存,在100TB数据的体量下,元数据小于10MB,可以满足任意数据规模,对于客户业务上的硬件使用无影响。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

pytorch 训练_基于文件存储UFS的Pytorch训练IO优化实践 的相关文章

  • Vue——vue3报错 <Suspense> slots expect a single root node.

    解决 如果有多组件嵌入时需要给每个组件一个 div 标签
  • springboot最新稳定版本、springcloud对应版本的选择

    1 登录springboot官网 查看当前最稳定版本 https spring io projects spring boot learn 可以看到目前为止最稳定的最新版本是2 4 0 2 登录springcloud官网 查看当前最新的稳定
  • 鸿蒙系统应用开发入门HelloWord(DevEco Studio怎么启动项目以及程序的运行过程)

    使用DevEco Studio新建项目之后 会自带HelloWorld 其他语言需要我们自己写 由于项目使用的模拟器不是在我们本地 而是部署在华为的服务器中 所以我们需要登录华为账号并实名认证才可以使用 登录并启动项目 DevEco Stu
  • JAX-RS (REST Web Services) 2.0 requires Java 1.6 or newer.

    maven 项目出现JAX RS REST Web Services 2 0 requires Java 1 6 or newer 错误 解决办法 这个是eclipse的bug 可见这个链接 https bugs eclipse org b
  • Eclipse中使用SVN

    我的个人博客地址 opiece me 欢迎大家的访问 1 在Eclipse里下载Subclipse插件 方法一 从Eclipse Marketplace里面下载 具体操作 打开Eclipse gt Help gt Eclipse Marke
  • 从0实现基于Linux socket聊天室-增加数据库功能-5

    之前更新过从0实现聊天室的4篇文章 很多粉丝朋友还是觉得内容相对简单 本文一口君会在原有代码基础上增加数据库操作功能 后续文章还会增加文件传输功能 前面文章链接 从0实现基于Linux socket聊天室 多线程服务器模型 1 从0实现基于
  • 卷积的利用

    视频 https www bilibili com video BV1vE411h7W2 from search seid 14520040502419023311 卷积 https www bilibili com video BV1A4
  • Java 8 lambda 函数式编程

    目录 简介 Lambda 表达式 解析1 解析2 自定义 lambda 表达式 例子1 一行输出多个值 例子2 数值计算 例子3 函数中使用自定义lambda表达式 简介 函数式编程就是类似于这样的东西 class MyTest publi
  • 运维攻城狮面试题汇总

    面试题汇总 什么是运维 什么是游戏运维 1 运维是指大型组织已经建立好的网络软硬件的维护 就是要保证业务的上线与运作的正常 在他运转的过程中 对他进行维护 他集合了网络 系统 数据库 开发 安全 监控于一身的技术 运维又包括很多种 有DBA
  • Qt响应按键按下

    1 Qt的键盘事件 void keyReleaseEvent QKeyEvent 按键释放事件 void keyPressEvent QKeyEvent 按键按下事件 2 Qt可以响应的按键事件 单个按键 组合键 例如 crtl c 注意
  • 如何用Python进行股票预测,数据分析带你从小白开始

    在开始这个话题之前请先记住一句友情提醒 股市有风险 投资需谨慎 我们写这个文章并不是鼓励大家去入市 小编本人也不买股票 我们只是在探索Python在股票分析和预测上面能发挥什么样的作用 对于和数据打交道的数据科学家来说 预测证券市场走势远比
  • C语言常见错误分析

    C语言常见错误分析 错误分类 语法错 逻辑错 运行错 0 忘记定义变量 main x 3 y 6 printf d n x y 1 C语言的变量一定要先定义才能使用 2 输入输出的数据的类型与所用格式说明符不一致 int a 3 float
  • PRML_频率与贝叶斯(一)

    我们从数据中能得到以下信息 总体信息 总体所属分布或者所属的分布族带来的信息 样本信息 从总体中抽样得来的样本给我们提供的信息 以上两种信息进行的统计推断称为经典统计学 它的观点是把样本看成来自具有一定概率分布的总体 先验信息 在抽样之前
  • Echarts.js(二):多系列柱状图

    多系列柱状图大部分与多系列折线图相似 一 简单html布局 简单的html如下
  • SqlServer分页查询

    文章目录 这篇博客讲的是SQL server的分页方法 用的SQL server 2012版本 下面都用pageIndex表示页数 pageSize表示一页包含的记录 并且下面涉及到具体例子的 设定查询第2页 每页含10条记录 首先说一下S
  • 写出质量好软件的75条体会

    1 你们的项目组使用源代码管理工具了么 MVM 应该用 VSS CVS PVCS ClearCase CCC Harvest FireFly都可以 我的选择是VSS 郁也风 公司使用的是VSS 在网上与朋友玩的就是CVS了 咖啡 现在都在用
  • 领域驱动设计:领域事件

    文章目录 领域事件 识别领域事件 领域事件相关案例 领域事件总体架构 领域事件 领域事件是领域模型中非常重要的一部分 用来表示领域中发生的事件 一个领域事件将导致进一步的业务操作 在实现业务解耦的同时 还有助于形成完整的业务闭环 举例来说的
  • 【因子算法】——求一个数的因子、质因子、求两个数的公因子

    下面理清楚一些数学概念 因数 一个数 如果存在可以被它整除的数 则这些数都是该数的因数 规定0没有因数 1的因数是1 其他的比如4的因数有 1 2 4 因子 一个数 如果存在可以被它整除的数且这些数不包括它本身 则这些书都是该数的因子 规定
  • 异常处理:System.Xml.XmlException_缺少根元素

    System Xml XmlException 类型的未处理的异常在System Xml dll中发生 其他 缺少根元素 问题背景 可能是因为强制断电 导致电脑里文件出错 原本运行正常的程序出现报错 缺少根元素 处理方法 删除C Users

随机推荐

  • 使用 sCrypt 实现定期支付合约

    这里我们介绍一个定期支付合约 允许用户定期存入款项 并且收款者可以定期收取款项 合约实现 合约代码如下 contract Recurring Ripemd160 userPubKeyHash Address of the owner of
  • 【uni-app框架】Vue框架的生命周期之data属性、props属性、及其生命周期函数的执行顺序总结【兼容uni-app总结】

    第一 props参数是实时更新的 而created和data仅会执行一次 当每次重新跳转到当前页面的时候 这也是为什么叫做当前页面或当前组件声明周期 第二 在uni app框架中 APP端和H5端的一些区别 APP端不兼容的特性 H5是最权
  • echarts 重新渲染(重新绘制,重新加载数据)等

    转载于 https www cnblogs com Skrillex p 7904797 html
  • SVN版本控制与分支设置

    使用SVN Eclipse做软件版本控制 http cnshell blog sohu com 157502394 html
  • uvm_info信息定制

    1 uvm自带的打印信息国语繁重 不利于debug uvm info TESTCASE sformatf my case0 new UVM DEBUG UVM INFO home zl Desktop uvm study template
  • 算法分析与设计-分治算法

    在计算机科学中 分治法是一种很重要的算法 字面上的解释是 分而治之 就是把一个复杂的问题分成两个或更多的相同或相似的子问题 再把子问题分成更小的子问题 直到最后子问题可以简单的直接求解 原问题的解即子问题的解的合并 这个技巧是很多高效算法的
  • DSA数字签名算法及其实现

    一 实验目的 在掌握了ElGamal和Schorr数字签名算法的基础上 进一步地学习和掌握DSA签名算法 深入地理解该算法是如何降低了签名信息的长度 当其中一个重要参数选为512bit的素数时 ElGamal签名的长度为1024bit 而D
  • 【react】props的简写方式

    props的简写 用static 就可以把类的属性写在类的里面 div div div div
  • linux(centos)无中文输入,如何解决

    1 终端执行安装命令 yum install Chinese Support 2 如下图 多出Input method 3 点击进行配置 4 reboot重启系统 新建一个文本 试一下输入 问题出来 难道是当时新建虚拟机时没有选择增强型虚拟
  • 【Linux】僵尸进程的检测,清理和避免

    一 僵尸进程的产生 一个进程终止的方法很多 进程终止后有些信息对于父进程和内核还是很有用的 例如进程的ID号 进程的退出状态 进程运行的CPU时间等 因此进程在终止时 回收所有内核分配给它的内存 关闭它打开的所有文件等等 但是还会保留以上极
  • 九章算法

    两个排序的数组A和B分别含有m和n个数 找到两个排序数组的中位数 要求时间复杂度应为O log m n 在线评测地址 LintCode 领扣 说明 中位数的定义 这里的中位数等同于数学定义里的中位数 中位数是排序后数组的中间值 如果有数组中
  • docker容器为什么总会挂掉?

    最近使用docker启动nginx时总会自动退出 看了一些文章后解决了问题 也明白了一些道理 将这些知识总结一下 只使用命令 docker run nginx就会自动退出 需要增加个死循环while true do echo hello s
  • 手把手教你编写Python抢购脚本

    想买mate40 但总是抢不到 所以想试着能不能写个脚本代码 第一步 把想要抢购的商品加进购物车 注意 脚本是对购物车内全部商品进行下单操作 所以不够买的商品最好先从购物车内删除 第二步 写好Python脚本 在抢购之前运行 并设置好抢购时
  • python基础教程:包的创建及导入

    包是一种通过用 带点号的模块名 来构造 Python 模块命名空间的方法 例如 模块名 A B 表示 A 包中名为 B 的子模块 正如模块的使用使得不同模块的作者不必担心彼此的全局变量名称一样 使用加点的模块名可以使得 NumPy 或 Pi
  • 数据库索引中包含的数据结构有哪些

    1 索引介绍 MySQL官方对索引的定义为 索引 Index 是帮助MySQL高效获取数据的数据结构 提取句子主干 就可以得到索引的本质 索引是数据结构 我们知道 数据库查询是数据库的最主要功能之一 例如下面的SQL语句 SELECT FR
  • 设计模式_单例模式

    线程的单例模式 一 懒汉式线程单例模式 以下该实例是懒汉式的单例模式 线程不安全 public class Singleton private static Singleton singleton private Singleton 设置构
  • Pid文件和路径

    Pid文件和路径 var run通常是存放pid文件的位置 var run是tmpfs文件系统 每次重启的时候都会清空 其中 var run是 run的链接 由于每次都清空 所以 如果想在 var run下面的子目录创建pid文件的话 子目
  • 高精度车牌识别算法

    一 车牌识别概述 车牌识别属于OCR的一种 但它也有自己的特点 考虑到边缘设备部署 我们没有用lstm 仅用普通的卷积层便实现了高精度的车牌识别方案 车牌识别的应用场景也十分广泛 常见的停车场收费系统 车牌识别算法也是智能交通算法中的基础算
  • 杭州乐园一日游攻略

    交通路线 杭州乐园就在湘湖边上 在地铁一号线的湘湖站下车后还有几公里路程 可以选择打车或者公交 门票购买 微信公众号 淘宝 美团都有卖的 可以都看看 有的平台会有优惠 时间安排 1 早上越早去越好 排队的人少一点 2 去的时候先看看过上车排
  • pytorch 训练_基于文件存储UFS的Pytorch训练IO优化实践

    我们在协助某AI客户排查一个UFS文件存储的性能case时发现 其使用的Pytorch训练IO性能和硬件的IO能力有很大的差距 后面内容有具体性能对比数据 让我们感到困惑的是 UFS文件存储 我们使用fio自测可以达到单实例最低10Gbps