GCC中的自动向量化(1)

本文是阅读Dorit Naishlos的文章“Autovectorization in GCC”时做的笔记。

在使用了语法树上的静态单赋值(tree SSA)优化框架之后,GCC已经具备了支持自动向量化的能力。目前对向量化的一个限制是,向量化必须在不存在迭代间数据依赖的前提下才能实施。
SIMD的向量化与传统向量机的不同在于,SIMD每次向量化的数组元素较少,随着数组元素类型的不同,可以向量化的数组元素数目也有变化,这个叫做向量化因子。
GCC中的自动向量化可以分为两类。

  • 针对循环的。处理循环中不同迭代之间的数据并行。
  • 针对普通串行代码的。这部分的并行技术叫做SLP(Superword Level Parallelism)。

传统自动向量化技术与SIMD自动向量化技术的区别。

  • 传统的技术主要是面向Fortran语言的科学计算程序。而SIMD则更侧重于C语言。C语言中的指针机制会带来新的问题。
  • SIMD架构下的内存结构要弱一些,对能够自动向量化的代码有着更严苛的要求。
    • 只能访问连续内存,要求向量长度倍数的对齐。
    • 有的平台下面有一些处理这些内存问题的机制,但是往往比较难以使用并且具有较高的开销。
    • SIMD指令不够通用和规范 。有些操作是与领域相关,有些只作用于某些数据类型上,不同架构的指令又不相同。

GCC中的数据依赖分析与向量化

数据依赖分析主要通过三个步骤。

  • 建立数据依赖图DDG(Data Dependence Graph)。
    • nodes: 代表loop语句。
    • edges: 表示依赖关系,可以是标量之间,也可以是内存引用之间。
  • 内存相关性的检测,可以通过比较下标的相关性测试来判断,在(tree-data-ref.c文件中实现)。
  • 通过检测DDG中的SCC(Strong Connected Components)来判断是否可以自动并行。

为解决SCC带来的无法向量化问题,可以使用loop distribution操作来把一个循环中的语句拆开,一个SCC对应一个循环(这个技术在作者写作本文时尚未在GCC中实现,但是目前已经实现)。
选项-ftree-vectorize同时使能了两个选项:ftree-loop-vectorize和ftree-slp-vectorize。前者实现循环中的自动向量化,后者实现串行代码中的自动向量化。
实现循环向量化的主体函数是vectorize_loops,它主要分为分析(vect_analyze_loop)和转换两个部分(vect_transform)。

  • vect_analyze_loop
    • vect_analyze_loop_form
    • analyze_data_refs
    • analyze_scalar_cycles
    • analyze_data_ref_dependence
    • analyze_data_ref_accesses
    • analyze_data_ref_alignment
    • analyze_operation
  • vect_transform
    • vect_transform_stmt
    • vect_transform_loop_bound

自动向量化对循环的形式是有要求的,需满足如下条件。

  • 循环的执行次数是可以预测的。
  • 只能处理最内层的循环。
  • 目前只支持只包含一个基本块的循环。对于某些简单的if-then-else结构,可以通过使用条件操作来把该结构转换成为一个基本块。

数据引用分析函数analyze_data_refs,负责找到所有的内存引用,并且检查它们是否是可分析的,也即建立一个访问函数。主要分析内容如下。

  • 内存依赖。
  • 访问模式。
  • 对齐分析。

标量的依赖环分析。需要分析循环体中的标量之间的依赖环,并进一步采取手段去打破它们。
操作分析函数analyze_operations。扫描所有的操作,决定向量化因子。目前的自动向量化机制具有下属约束。

  • 每个平台只支持一个向量长度。
  • 每个循环只支持一种数据类型的自动向量化。

向量化的转换主要分为以下步骤。

  • 从头至尾扫描循环体中的语句。
  • 为每个语句增加一个指向向量化后语句的指针。
  • 显式地删除原始的store语句。
  • 其他的语句在死代码删除优化中被删除掉。

也存在着其他的一些情况,而无法对语句进行一对一的向量化。

内存引用的处理

目前主要处理两种内存引用。

  • ARRAY_REFs
  • INDIRECT_REFs

为了提高并行度,增大自动向量化的机会,可以进行一些增强型的数据流分析。

  • 使用更复杂的数据依赖测试。
  • 去掉那些距离大于向量化因子的引用。
  • 通过重新排列结点来尝试去掉一些依赖。

当存在指针别名的时候,仍然可以进行向量化,但需要用runtime_overlaptest来保证向量化的正确性。
内存访问若连续,则可以直接向量化。否则,就需要进行特殊的数据操作,然后把它们装载到一个向量中。

  • gather/scatter操作。
  • 把两个向量中的数据打包(pack)到一个向量中。
  • 在数据元素上使用交换操作。
  • 为特定的访问模式提供支持。

函数analyze_access_pattern验证访问模式是否能被向量化。当前只支持连续数据访问。
处理对齐的几个不同级别。

  • 静态对齐分析。
  • 通过变换来强迫对齐。
  • 特殊的指令来支持高效的不对齐访问。

    强制对齐数据引用(enforce_data_refs_alignment)的手段。

    • loop versioning。循环的多版本。
    • loop peerling。循环剥离。

处理不对齐的两个步骤。

  • 有一个计算对齐差距的函数。
  • 再有一个函数,按照对齐的差距,使用两个vector指针,来生成一个vector。

目前处理自动向量化有两个自相矛盾的目标。

  • 较高层的中间表示(在语法树上处理)。
  • 要描述一些低级别的特性(例如对齐等)。

判断一个循环是否能够被向量化,从以下三个方面。

  • loop form
  • data reference
  • operation

当前可以进行向量化的loop必须具备的特点。

  • 最内层的循环,只有单个基本块。
  • 只有连续的数组访问,并且保持对齐。
  • 没有可以产生标量环的操作,所有的操作都施加于同一数据类型,并且可以用现有的语法树操作码来表示。

未来的工作

支持更多的loop形式。

  • unknown loop bound。
  • if-then-else结构。

支持更多的数据引用类型。

  • 增强数据依赖的测试。
  • 支持其他的数据访问模式。
  • 利用数据重用。

支持更多的操作。

  • 支持作用于不同数据类型上的操作,包括强制类型转换cast。这需要有data packing和unpacking的支持。
  • 支持对于induction和reduction等操作的向量化。需要使用target hook或者新的tree code。

其他的优化。

  • 同一平台上支持多个vector length。
  • 建立向量化的代价模型。
  • 与其他优化或者pass的接口。
    • loop parallelism.
    • SLP pass.
  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值