0
点赞
收藏
分享

微信扫一扫

从hex_encode起利用SIMD向量指令引入PgVA aka PostgresVectorAcceleration(一)


从hex_encode起利用SIMD向量指令引入PgVA aka PostgresVectorAcceleration

PostgreSQL邮件列表对SIMD加速执行引擎进行介绍。

目的

受集成JIT以实现加速执行器的启发,我认为使用现代硬件的SIMD指令可以显著加速面向数组数据的简单算法。我想通过hex_encode例子介绍这样的编程风格:

1) 对数组进行操作(字节)

2) 简单算法

3) 在某些情况下会部分限制性能(例如pg_dump)

实施指南

主要目标是利用硬件提供的现有资源来加速常见硬件上的通用案例,以下指南介绍第一个实施。

1) 限制64位架构:这些是主要的服务器架构,具有必要的数据格式和相应的寄存器和操作指令

2) 从Intel x86-64的SIMD指令开始:这是开发和实际使用的最常用的平台

3) 不要将概念仅限于Intel x86-64,以便以后对其他架构有更多经验的人可以加入并实现类似的算法

4) 用汇编语言实现leaf函数/过程:这些主要由一个主循环组成,无需调用子例程或额外的分支

5) 为最大限度的使用硬件而不是优雅的编程:一旦开始编码测试,简单的算法就可以向宣传那样公众,并用于替换C标准函数的大多执行部分

6) 通过将其集成到特定子历程(此处为hex_encode)中来隔离占用空间:这确保满足快速执行的要求(例如缓冲区大小),并且不需要像在库例中那样重复检查

7) 通过避免等待延迟来保持向量化执行端口一直在做有用的工作

8) 以cache line方式访问内存(从输入缓冲区读取,写到输出缓冲区)避免内部的cache问题

9) 重点通过最先进的SIMD指令集AVX512进行优化:提供了最先进的指令和相当多的大寄存器帮助避免延迟

10) 如果可能用老的SIMD指令集(AVX2或SSE2)进行fallback实现

实施细节

1) 使用NASM编译器写循环算法:NASM得到积极维护,由多种输出格式,遵循Intel风格,实现了所有当前的指令并且速度很快

2) 循环独立于操作系统,因此支持所有基于NASM obj输出格式的操作系统:包括Linux和windows

3) 算法使用先进技术(常量和临时寄存器)来避免大多数不必要的内存访问:汇编实现让您可以完全控制寄存器(与内联函数不同)

4) 多个依赖链交错工作以最大程度减少延迟:编码通常穿插并使用几乎所有可用的寄存器

5) 一些指令(moves、zeroing)在处理器执行端口外执行:这些不消耗执行周期但是需要考虑他们的延迟

6) 一些向量指令(乘加)的延迟是5,例如,这意味着在指令发出后,处理器必须等待5个周期,直到结果可以在依赖链中使用。为避免这种情况并保持所有向量执行端口(P0和p5)繁忙,必须由9个其他指令在工作和算法的其他流中间以最大限度提高硬件使用和整体性能

7) 所有循环都实现为单独的C可调用函数(根据OS调用约定):他们都是leaf函数,不调用其他子例程

8) 在调用方由一个特殊的调度程序完成选择哪个实现:调用这处理架构能力(可用指令集),了解需要的工作,通常需要一个合适的最小工作量来有效调用一个提供的实现

9) 循环至少运行2-4次以补偿初始化开销:这意味这基于特定SIMD实现的最小工作量

10) 循环在检测到错误(例如错误的输入数据)后终止并返回成功完成的工作量:标准线性实现接管已经简历的错误处理

11) 循环在最后有一些额外的输出缓冲区以最佳方式工作,以便能够在最后一轮中超越:尽管如此,正确的工作量返回给调用者,并且在实际结果之后的输出缓冲区的向量大小清零

12) 循环可能会在输入缓冲区之后预加载一些数据,但确保永远不会超过以下页面边界以避免任何访问冲突:这对内存系统没有伤害,因为输出缓冲区最后由一个补充缓冲区,但这可能如果认为不支持,则更改为将尾部处理留给标准实现。

举报
0 条评论