Halide: 一种用于优化图像处理管道中的并行性、局部性和重新计算的语言和编译器

Halide: A Language and Compiler for Optimizing Parallelism,
Locality, and Recomputation in Image Processing Pipelines

他人笔记:https://blog.csdn.net/tiaozhanzhe1900/article/details/102812485

Halide简介:https://blog.csdn.net/weixin_42261213/article/details/100030830

zz图像、神经网络优化利器:了解Halide:https://my.oschina.net/u/4342183/blog/3420173

Halide的官网:https://halide-lang.org/

https://github.com/opencv/opencv/wiki/DNN-Efficiency

https://github.com/halide/Halide

摘要

图像处理管道结合了模板计算和流程序的挑战。它们由具有不同模板阶段的大型图、复杂的缩减图和具有全局或依赖数据的访问模式的阶段组成。由于其复杂的结构,一个简单的管道实现和一个优化的管道实现之间的性能差异通常是一个数量级。高效的实现需要并行性和局部性的优化,但是由于模板的特性,并行性、局部性和引入共享值的冗余重计算之间存在着基本的紧张关系。

我们提出了一个系统模型的权衡空间的基本模板管道;一个时间表表示,该表示在这个空间中 针对图像处理管道的每个阶段来描述具体点;以及用于Halide图像处理语言的优化编译器,该编译器从Halide算法和时间表中综合高性能实现。将此编译器与调度空间上的随机搜索相结合,可以使简洁、可组合的程序在广泛的真实图像处理管道上以及跨不同的硬件体系结构(包括带有SIMD的多核处理器和异构CPU+GPU执行)上实现最先进的性能。在几个小时内编写的简单Halide程序中,我们展示了比专家们在数周或数月内优化的手工优化的C、内联函数和CUDA实现快5倍的性能,适用于过去自动编译器无法企及的图像处理应用程序。

关键词:领域特定语言;编译器;图像处理;局部性;并行性;冗余计算;优化;GPU;矢量化

1. 介绍

图像处理管道无处不在,对于捕捉、分析、挖掘和渲染由无数摄像头和基于图像的传感器收集的视觉信息流至关重要。从原始处理到目标检测和识别,到微软的Kinect,到Instagram和Photoshop,再到医学成像和神经扫描,所有的应用都要求极高的性能,以应对图像传感器的分辨率和帧速率的迅速提高以及算法的日益复杂。同时,不断缩小的摄像机和移动设备需要极高的效率才能在电池供电的情况下持续几分钟以上。虽然耗电量大的收音机和视频编解码器可以在定制硬件中实现缓慢变化的标准,但图像处理管道正在迅速发展和多样化,需要高性能的软件实现。

图像处理管道结合了模板计算和流程序的挑战。它们由许多不同操作的大型图形组成,其中大多数是模板计算。这些管道同时又宽又深:每个阶段在它必须处理的许多像素上都表现出数据并行性,整个管道由不同操作的长序列组成,这些操作的算术强度很低(执行的计算与从前一级读取并写入后级的数据的比率)。例如,一个最近的算法,局部拉普拉斯滤波器[3,22 ] 的实现是99个不同阶段的图(图1),包括许多不同的模板和大的依赖于数据的重采样。

在这里插入图片描述

图1。图像管道使用大量相互连接的异构级。这里我们展示了局部拉普拉斯滤波器的结构[3,22],它用于摄影后期制作中的各种任务。每个框表示中间数据,每个箭头表示定义该数据的一个或多个函数。该管道包括水平和垂直模板、重采样、依赖于数据的聚集和简单的逐点函数。

由于这种结构,给定管道的原始实现与高度优化的实现之间的性能差异通常是一个数量级或更多。在当前的工具中,达到最高性能的唯一方法通常是在低级C、CUDA、内联函数和汇编中手工编写并行、矢量化、平铺和全局融合的代码。简单的管道变成了成百上千行错综复杂的交错代码;复杂的管道,比如Adobe的Camera Raw引擎,变成了数十万行。对它们进行调优需要专家程序员付出巨大的努力,而且最终的结果既不能移植到不同的体系结构中,也不能与其他算法组合在一起,同时也不能牺牲这些辛苦获得的性能。优化的子程序库也不能解决问题,因为许多关键的优化都涉及到跨阶段的生产者-消费者局部性的融合。

我们通过提高抽象级别、将算法定义与其执行策略解耦来解决这一挑战,以提高可移植性和可组合性,同时自动搜索结果管道到并行机和复杂内存层次结构的优化映射。有效的抽象和自动优化使简单的程序能够获得比手动调整的专家实现更高的性能,同时运行在广泛的体系结构中。

1.1 图像处理管道

在科学应用中,模板以迭代模板计算的形式得到了很好的研究,其中一个或几个小模板在多次迭代中应用于同一网格中[10,16,19 ]。相比之下,我们对其他应用感兴趣,比如图像处理和计算机图形学,这些领域的模板很常见,但通常以一种非常不同的形式:模板管道。模板管道是不同模板计算的图形。同一个模具会发生迭代,但这是例外,而不是规则;大多数阶段在将数据传递到下一个阶段之前只应用一次模具,下一个阶段在不同的模具上执行不同的数据并行计算。

图结构程序已经在流式语言的背景下进行了研究[4,11,29 ]。静态通信分析允许流编译器通过交叉计算和内核之间的通信来同时优化数据并行性和生产者-消费者局部性。然而,大多数流编译研究都集中在1D流上,其中滑动窗口通信允许1D模板模式。图像处理管道可以看作是二维和三维流和模板上的程序。图像处理所需的计算模型也比模板更通用。虽然大多数阶段都是对先前阶段的结果进行点或模板操作,但有些阶段从任意数据相关地址收集,而其他阶段则分散到任意地址以计算像直方图之类的操作。

简单地图操作的管道可以通过传统的循环融合进行优化:将每个点上的多个连续操作合并为一个单一的复合操作,通过最大化生产者-消费者局部性,在流经管道时将中间数据值保存在快速本地内存(缓存或寄存器)中,从而提高了运算强度。但传统的循环融合不适用于模板操作,即消费者阶段中的相邻点依赖于生产者阶段的重叠区域。相反,模板需要在生产者-消费者位置、同步和冗余计算之间进行复杂的权衡。因为这种权衡是通过交错分配、执行和每个阶段的通信顺序来完成的,所以我们称之为管道调度。在科学应用中,这些权衡存在于调度单个迭代模板计算中,并且选择空间的复杂性通过在过去的工作中引入的许多不同的平铺和调度策略来反映[10,16,19 ]。在图像处理管道中,这种权衡必须针对图中各个阶段之间的生产者-消费者关系(通常是几十个或数百个)进行,而理想的时间表取决于每个阶段之间的全局交互作用,通常需要许多不同策略的组合。

1.2 贡献

Halide是一种开源领域专用语言,用于现代计算摄影和视觉应用中的复杂图像处理管道[26 ]。在本文中,我们提出了这种语言的优化编译器。我们介绍:

  • 模板管道中局部性、并行性和冗余重新计算之间权衡的系统模型;
  • 跨越这个选择空间的调度表示;
  • 一种基于这种表示法的DSL编译器,它结合了Halide程序和调度描述,在这个空间的任何地方合成点,使用一种设计,在这种设计中,如何执行一个程序的选择不仅与要计算什么的定义分离,而且被完全拉到编译器的黑匣子之外;
  • 一种基于简单区间分析的数据并行流水线循环合成器,它比多面体模型更简单,表达更少,但在所能分析的表达式类中更通用;
  • 一种代码生成器,它使用比多面体模型简单得多的机器,为图像处理管道生成高质量的矢量代码;
  • 以及一个自动调整器,可以推断出高性能的调度比专家编写的手工优化程序快5倍——用于复杂的图像处理管道,使用随机搜索。

我们的调度表示在局部性、并行性和避免冗余工作之间建立了一系列折衷模型。当然,也可以用先前的模板组合来表达。与以往的模板代码生成系统不同,它不只是描述一个模板调度策略,而是在模板图和其他图像处理计算中分别处理每个生产者-消费者边缘。

我们的拆分表示法将调度与底层算法分离,再加上编译器的由内而外的设计,使编译器能够自动搜索最佳调度。可能的时间表空间是巨大的,有数百个相互依赖的维度。对于现有的模板编译器和自动调谐器所采用的多面体优化或穷尽性参数搜索,维数太高。然而,我们证明了使用随机搜索可以发现高质量的调度。

给定一个时间表,我们的编译器会自动合成用于x86和ARM cpu的高质量并行矢量代码(SSE/AVX和NEON),以及与主机管理代码交织在一起的CUDA内核图形,以执行混合GPU。它使用简单但通用的区间分析自动推断所有内部分配和完整的循环嵌套[18 ]。直接将数据并行维度映射到SIMD执行,包括对跨步访问模式的仔细处理,可以生成高质量的向量代码,而不需要任何通用的循环自动矢量化。

最终的结果是一个系统,它使简洁、可组合的程序能够在广泛的真实图像处理管道上实现最先进的性能,并跨越不同的硬件体系结构,包括具有SIMD的多核和异构CPU+GPU执行。在几个小时内编写的简单Halide程序中,我们展示的性能比专家在数周或数月内编写的手工调整的C、内联函数和CUDA实现快5倍,适用于过去自动编译器无法实现的图像处理应用程序。

2. The Halide DSL

我们使用Halide DSL以简单的功能风格描述图像处理管道[26 ]。局部拉普拉斯滤波器的简单C++实现(图1)由几十个循环嵌套和数百行代码描述。这对于用传统的循环优化系统进行全局优化是不现实的。Halide版本将其提取成62行,仅描述99级管道中的基本数据流和计算,所有关于程序如何合成的选择都被单独描述(第3节)。

在Halide中,命令式语言中可变数组的值是从坐标到值的函数。它将图像表示为定义在无限整数域上的纯函数,其中函数在某一点的值表示相应像素的颜色。管道被指定为函数链。函数可以是参数中的简单表达式,也可以是有界域上的约化。定义函数的表达式没有副作用,与任何简单函数语言中的表达式非常相似,包括:

  • 算数和逻辑运算
  • 从外部图像加载;
  • if-then-else 表达式;
  • 对命名值的引用(可以是函数参数,也可以是由函数 let 构造定义的表达式);
  • 调用其他函数,包括外部C ABI函数。

例如,一个可分离的3x3非标准化盒形滤波器表示为x;y中的两个函数链:

UniformImage in(UInt(8), 2)
Var x, y
Func blurx(x,y) = in(x-1,y) + in(x,y) + in(x+1,y)
Func out(x,y) = blurx(x,y-1) + blurx(x,y) + blurx(x,y+1)

这种表示比大多数函数式语言简单。它不包括高阶函数、动态递归或其他数据结构(如列表)。从标量函数简单地映射到标量坐标。更高级功能(如高阶函数)的约束版本被添加为语法糖,但不会更改底层表示。

这种表示形式足以描述各种图像处理算法,并且这些约束使得在编译过程中能够灵活地分析和转换算法。关键的是,这种表示在每个函数的域内自然是数据并行的。另外,由于函数定义在一个无限域上,可以根据需要通过计算任意的附加值保护带来安全有效地处理边界条件。保护带是图像处理代码中的一种常见模式,既考虑到对齐等性能问题,也考虑到了安全性。当特定的边界条件对算法的意义有影响时,函数可以定义自己的边界条件。

减法功能。 为了表示像直方图和一般卷积之类的操作,Halide还需要一种表示迭代或递归计算的方法,如求和、直方图和扫描。减法分为两部分:

  • 一种初始值函数,它指定输出域中每个点的值。
  • 一种递归归约函数,它根据函数的先验值重新定义输出坐标表达式给定点的值。

与纯函数不同,归约的意义取决于归约函数的应用顺序。程序员通过定义一个归约域来指定顺序,该域由每个维度的最小和最大表达式限定。输出域中每一点上的值由该点上的归约函数的最终值定义,然后按字典顺序在整个归约域中递归。

这种模式可以描述一系列超出传统模板计算范围的算法,但对图像处理管道至关重要,在某种程度上限制了副作用。例如,直方图均衡化结合了多次减少和数据相关的聚集。散射缩减计算直方图,递归扫描将其集成到CDF中,逐点操作使用CDF重新映射输入:

UniformImage in(UInt(8), 
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Halide 是一个开源的图像处理计算机视觉 DSL(领域特定语言),其目的是让程序员更加轻松地编写高性能的图像处理代码。Halide 的特点是具有易于使用的语法、高性能的代码生成以及可移植性。 本系列文章将介绍 Halide 的基本语法和使用方法,并通过一些实例来演示如何使用 Halide 进行图像处理计算机视觉任务。 第一篇文章将介绍 Halide 的基本概念和安装方法。 ## 什么是 Halide? Halide 是由丹尼尔·瑞德福(Daniel R. Johnson)和 Jonathan Ragan-Kelley 在 MIT 开发的一个开源项目。它是一个用于编写高性能图像处理计算机视觉代码的 DSL。 Halide 的主要目标是使程序员能够使用一种简单易懂的语法编写高性能的代码,而无需了解 CPU 或 GPU 的细节。Halide 支持多种平台,包括 x86、ARM、MIPS 和 PowerPC 等 CPU,以及 NVIDIA、AMD 和 ARM 等 GPU。 Halide 的核心概念是“函数”。函数可以看作是一组描述了如何对输入数据进行处理的指令集合。这些指令可以被 Halide 编译成高效的 CPU 或 GPU 代码,并在运行时执行。 ## Halide 的安装方法 Halide 可以在 Linux、macOS 和 Windows 等操作系统上运行,并且支持多种编译器,包括 GCC、Clang 和 MSVC 等。下面是在 Ubuntu 20.04 上安装 Halide 的步骤: 1. 添加 Halide 的 PPA: ``` sudo add-apt-repository ppa:halide/ppa ``` 2. 更新软件包列表: ``` sudo apt-get update ``` 3. 安装 Halide: ``` sudo apt-get install libhalide-dev ``` 安装完成后,可以使用以下命令检查 Halide 是否已经安装成功: ``` pkg-config --cflags --libs Halide ``` 如果输出了一些 Halide 相关的信息,则表示 Halide 安装成功。 ## 总结 本篇文章介绍了 Halide 的基本概念和安装方法。下一篇文章将介绍 Halide 的基本语法和使用方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值