OneFlow vs PyTorch:谁在Permute上更快?
OneFlow vs PyTorch:谁在Permute上更快?
近日,一篇技术文章详细介绍了如何通过OneFlow实现比PyTorch快6倍的Permute/Transpose算子。无论是在NLP领域的Transformer模型还是视觉领域的新秀Vision Transformer中,Permute算子都扮演着重要角色。文章不仅深入探讨了Permute算子的原理及其CUDA实现对网络训练速度的影响,还展示了OneFlow在这一方面的深度优化成果,其带宽利用率接近原生Copy操作,远超PyTorch。这一发现引发了技术圈内的广泛关注和讨论。
Permute算子的作用与原理
Permute算子的作用是变换张量数据维度的顺序。例如,对于一个形状为(2, 3)的张量,通过Permute操作可以将其维度顺序进行交换:
x = flow.randn(2, 3)
y = x.permute(1, 0)
print(y.shape) # 输出:(3, 2)
从数学角度理解,输出Tensor的第i维对应输入Tensor的dims[i]维。然而,在实际物理设备上,张量的数据是保存在一块连续的内存中。因此,Permute操作需要通过以下步骤实现:
- 通过当前输出的一维偏移量(offset)计算对应的高维索引
- 根据参数dims重新排列输出索引,进而得到输入索引
- 将输入索引转换成输入偏移量
- 最后进行数据移动
为了简化这一过程,OneFlow提供了一个工具类NdIndexOffsetHelper,用于方便地进行一维偏移量和高维索引之间的转换。
OneFlow的优化策略
OneFlow通过多项优化策略显著提升了Permute算子的性能:
IndexType静态派发:随着深度学习模型规模的扩大,参与运算的元素数量可能超过int32_t的表示范围。OneFlow通过模板参数IndexType,根据元素数量动态选择int32_t或int64_t,以优化除法运算的效率。
合并冗余维度:在一些特殊情况下,Permute维度可以进行合并:
- 大小为1的维度可以直接去除
- 连续排列的维度可以合并成一个维度
例如,对于一个四维Permute操作:
x = flow.randn(3, 4, 5, 6)
y = x.permute(2, 3, 0, 1)
print(y.shape) # 输出:(5, 6, 3, 4)
可以将其视为二维Permute操作:
x = x.reshape(x.shape[0]*x.shape[1], x.shape[2]*x.shape[3])
y = x.permute(1, 0)
y = y.reshape(x.shape[2], x.shape[3], x.shape[0], x.shape[1])
这种合并可以减少计算量,提升速度。
- 使用更大的访问粒度:通过增加数据访问的粒度,进一步优化访存效率。
性能对比
经过上述优化,OneFlow在Permute操作上的性能显著优于PyTorch:
- 带宽利用率接近原生Copy操作
- 在某些情况下,速度比PyTorch快6倍
这一性能提升对于深度学习模型的训练速度具有重要意义,特别是在Transformer类模型中,Permute算子被频繁使用。
实际应用场景
Permute算子在现代深度学习模型中有着广泛的应用:
- Transformer模型:在多头注意力机制中,需要Permute算子来改变数据维度排布
- Vision Transformer:用于图像数据的维度重排,以适应模型输入要求
OneFlow在Permute算子上的优化,不仅提升了单个操作的性能,更有助于整个模型训练的加速。
结论
OneFlow通过深度优化Permute算子,实现了显著的性能提升。其优化策略包括IndexType静态派发、维度合并以及增大访问粒度等。这些优化使得OneFlow在Permute操作上的带宽利用率接近原生Copy操作,远超PyTorch。对于深度学习开发者来说,这一优化成果具有重要的参考价值。