X-SIMD高性能跨平台向量化加速库

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介: X-SIMD是平头哥基于开源SIMDe开发的一个header-only C程序库,提供了一种简单易用的跨平台SIMD程序优化方案,旨在为不支持SIMD指令集的平台提供SIMD支持。X-SIMD可以帮助开发者快速完成应用软件迁移arm平台,减少用户重新编写SIMD算法工作量。

背景

在众多计算型软件的代码中,例如大数据,HPC以及AI,视频编解码软件,底层都会使用SIMD(单指令多数据流)指令对核心的逻辑进行优化。SIMD是一种计算机指令集,用于在不同的数据上执行相同的指令。它充分利用处理器的并行流水线,允许将多个数据一起处理来加速计算,从而提高程序的并行性,实现性能飞升。然而在当前服务器领域,由于X86生态占据主导地位,大量的软件针对X86平台利用X86的SIMD指令例如SSE/AVX/AVX2/AVX512进行了热点优化。由于ARM架构的服务器正在快速发展阶段,软件生态还远不如X86成熟,当一个X86软件需要移植到ARM服务器平台时,尤其是带有Intel SIMD的intrinsic指令时,需要将Intel的SIMD intrinsic指令(SSE/AVX/AVX2/AVX512)用ARM的SIMD intrinsic指令(NEON/SVE)重写,这将会给软件移植工作带来巨大的工作量,同时由于这些intrinsic指令和业务逻辑紧密耦合,这些移植的工作不具备可复制性,这也给软件移植带来了很重的负担。

X-SIMD简介

X-SIMD是平头哥基于开源SIMDe开发一个header-only C程序库,提供了一种简单易用的跨平台SIMD程序优化方案,旨在为不支持SIMD指令集的平台提供SIMD支持,它支持以下x86和arm指令集:

  • x86 SSE,SSE2,SSE3,SSSE3,SSE4.1,SSE4.2, AVX, AVX2,AVX512
  • aarch64 NEON, SVE

X-SIMD具有的优势:

  • 良好的可移植性,X-SIMD为不同的硬件平台提供一个通用的API,它可以屏蔽不同平台之间的差异性,让开发者能够更加方便地使用SIMD指令,从而实现跨平台的应用开发;另一方面,对于已经包含许多intel intrinsic指令的软件来说,X-SIMD只需要通过头文件和编译选项修改,便可以轻松实现x86到arm平台的迁移。
  • 高效的性能:X-SIMD充分利用不同硬件平台上的SIMD指令集,从而实现高效的数据并行计算;另外X-SIMD的API 内部实现有多种方式,或SVE,或NEON,或C语言,我们通过大量benchmark工作,按照最优实现方式,提供出去。测试对比,X-SIMD实现的接口对比华为Avx2Neon平均高出5~150%不等。
  • 良好的兼容性:X-SIMD提供5000+ API,覆盖了intel 大多数指令集,并且每种接口至少包含一种以上实现方式,也就c语言实现,它保障了用户软件从x86迁移arm时,软件可以正常运行。
  • 很好的可读性和可维护性,X-SIMD是由C语言编写的,代码结构清晰,开发者可以轻松地使用它来实现向量化计算,且易于扩展。
  • 简单易用:如上所述,通过少量修改,X-SIMD帮助应用软件完成迁移,减少用户重新编写SIMD算法工作量,因此X-SIMD自推出就在HPC、大数据、数据库等场景得到应用。

在本文中,我们将主要介绍X-SIMD的架构和应用场景。

X-SIMD软件架构

X-SIMD软件架构图

如上图,X-SIMD是模块化的,在X-SIMD中x86和arm架构的接口实现分别有自己的头文件和命名空间,所有X-SIMD接口都以xsimd_为前缀。前端是x86指令集,后端是arm指令集和C语言实现,软件迁移时,无需更改x86的Native SIMD相关指令,经过X-SIMD重命名技术后会自动调用以xsimd_为前缀的API,X-SIMD按照最优实现将对应的x86指令翻译为SVE、NEON或C语言实现,举个例子,_mm512_add_ps函数实现如下:

XSIMD_FUNCTION_ATTRIBUTES
xsimd__m512
xsimd_mm512_add_ps (xsimd__m512 a, xsimd__m512 b) {
  #if defined(XSIMD_X86_AVX512F_NATIVE)
    // 如果当前平台是x86,支持AVX512F,则直接运行native API
    return _mm512_add_ps(a, b);
  #elif defined(XSIMD_ARM_SVE_NATIVE)
    // 如果是arm 平台,则优先走SVE实现分支,(因为经我们测试比NEON实现性能更好点)
    xsimd__m512 r;
    xsimd_svbool_t pg = svptrue_b32();
    r.sve_f32[XSIMD_SV_INDEX_0] = svadd_f32_z(pg, a.sve_f32[XSIMD_SV_INDEX_0], b.sve_f32[XSIMD_SV_INDEX_0]);
    r.sve_f32[XSIMD_SV_INDEX_1] = svadd_f32_z(pg, a.sve_f32[XSIMD_SV_INDEX_1], b.sve_f32[XSIMD_SV_INDEX_1]);
    r.sve_f32[XSIMD_SV_INDEX_2] = svadd_f32_z(pg, a.sve_f32[XSIMD_SV_INDEX_2], b.sve_f32[XSIMD_SV_INDEX_2]);
    r.sve_f32[XSIMD_SV_INDEX_3] = svadd_f32_z(pg, a.sve_f32[XSIMD_SV_INDEX_3], b.sve_f32[XSIMD_SV_INDEX_3]);
    return r;
  #elif defined(XSIMD_ARM_NEON_A64V8_NATIVE)
  // 否则优先走NEON实现分支
    xsimd__m512 r;
    r.m128[0].neon_f32 = vaddq_f32(a.m128[0].neon_f32, b.m128[0].neon_f32);
    r.m128[1].neon_f32 = vaddq_f32(a.m128[1].neon_f32, b.m128[1].neon_f32);
    r.m128[2].neon_f32 = vaddq_f32(a.m128[2].neon_f32, b.m128[2].neon_f32);
    r.m128[3].neon_f32 = vaddq_f32(a.m128[3].neon_f32, b.m128[3].neon_f32);
    return r;
  #else
    // 如果上述均未实现则走c语言实现分支。
    xsimd__m512_private
      r_,
      a_ = xsimd__m512_to_private(a),
      b_ = xsimd__m512_to_private(b);
      XSIMD_VECTORIZE
      for (size_t i = 0 ; i < (sizeof(r_.m256) / sizeof(r_.m256[0])) ; i++) {
        r_.m256[i] = xsimd_mm256_add_ps(a_.m256[i], b_.m256[i]);
      }
      return xsimd__m512_from_private(r_);
  #endif
}
// _mm512_add_ps 命名重定向
#if defined(XSIMD_X86_AVX512F_ENABLE_NATIVE_ALIASES)
  #undef _mm512_add_ps
  #define _mm512_add_ps(a, b) xsimd_mm512_add_ps(a, b)
#endif

X-SIMD目标是涵盖Intel AVX指令族,包括SSE、AVX,AVX2,AVX512,一共5588条指令,目前X-SIMD已完成常用接口API的开发约1000个,满足大数据,HPC,数据库等场景使用。

X-SIMD使用

使用X-SIMD开发非常简单,以下步骤既适用于跨平台开发,也适用于应用迁移场景:

  • X-SIMD是header only,直接拷贝x-simd文件夹到/usr/local/include目录下
  • 在代码中添加<xsimd/xsimd.h>头文件,或者将原来的x86 intrinsic头文件修改为 <xsimd/xsimd.h>
  • 在构建脚本中添加以下编译选项以下编译

-DXSIMD_ENABLE_NATIVE_ALIASES (用于API重命名)

-march=armv8.5-a+crc+sve2+sha2+sha3+sve2-sha3 -msve-vector-bits=128 (适用于arm平台)

-I /usr/local/include/x-simd

如果应用适用的cmake进行构建,可以添加以下编译项

option(ENABLE_X_SIMD "Enable X-SIMD optimized" OFF)
if (ENABLE_X_SIMD)
  MESSAGE( STATUS " ENABLE X_SIMD")
  add_definitions(-DXSIMD_ENABLE_NATIVE_ALIASES)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msve-vector-bits=128 -march=armv8.5-a+crc+sve2+sha2+sha3+sve2-sha3+sve2-aes")
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msve-vector-bits=128 -march=armv8.5-a+crc+sve2+sha2+sha3+sve2-sha3+sve2-aes")
  include_directories(/usr/local/include/x-simd)
endif()
  • 建议应用使用O2以上的编译优化选项

以下是X-SIMD使用一个示例,例如下面的代码使用X-SIMD SSE2指令集来计算两个向量的和

#include <stdlib.h>
#include <xsimd/xsimd.h>
int main()
{
    struct {
      int32_t a[4];
      int32_t b[4];
      int32_t r[4];
  } test_vec = {
      {  INT32_C(  1587156417),  INT32_C(  1768270179), -INT32_C(  1942404587),  INT32_C(   346970517) },
      {  INT32_C(  2141391970),  INT32_C(  1584534422),  INT32_C(  1144809083), -INT32_C(   446909148) },
      { -INT32_C(   566418909), -INT32_C(   942162695), -INT32_C(   797595504), -INT32_C(    99938631) }
  };
    __m128i a = _mm_loadu_epi32(test_vec.a);
    __m128i b = _mm_loadu_epi32(test_vec.b);
    __m128i r = _mm_loadu_epi32(test_vec.r);
    __m128i sum = _mm_add_epi32(a, b);
    if (memcmp(&sum, &r, sizeof(__m128i)) != 0) {
        printf("Example test failed.\n");
    } else {
        printf("Example test OK.\n");
    }
    return 0;
}

用户可使用以下编译命令在aarch64上进行测试。

gcc -O2 -fPIC -Wall test.c -o test_xsimd -I. -DXSIMD_ENABLE_NATIVE_ALIASES -msve-vector-bi

另外我们提供了一个脚本可以快速扫描项目代码中有多少x86_64 intrinsic指令,方便用户在做迁移时统计接口,以评估迁移工作量:

#!/usr/bin/sh
set -e
DIR=$pwd
DATE=`date +%Y%m%d-%H%M%S`
SSE="_mm(?:[0-9]+)?_\w+"
AVX="_mm256(?:[0-9]+)?_\w+"
AVX512="_mm512(?:[0-9]+)?_\w+"
if [ -n $1 ];then
  DIR=$1
  echo $DIR
fi
grep -oPrn $SSE $DIR &> sse.log.$DATE
grep -oPrn $AVX $DIR &> avx.log.$DATE
grep -oPrn $AVX512 $DIR &> avx512.log.$DATE

X-SIMD应用场景

X-SIMD适用于各种应用程序,例如数字信号处理、图像和视频处理、机器学习、科学计算等,这些应用软件属于计算密集型,需要处理大量的数据计算,往往会使用到SIMD进行优化。以下是几个典型的应用场景说明:

大数据场景OAP

优化分析包OAP(Optimized Analytics Package for Spark Platform )是英特尔和社区开发的开源项目,旨在提高 Spark 性能。它基于先进的英特尔硬件技术,提供了多种功能来改善 Spark 高速缓存、Shuffle、执行和机器学习性能。

其中Spark SQL 的 Native 引擎可以很好地处理基于行的结构化数据将 Spark 行数处理转为列式处理,Gazelle Plugin 基于 Apache Arrow 对 SIMD 友好的列式数据处理重新实现了 Spark SQL 执行层,并利用 Arrow 的 CPU 缓存友好的列式内存布局、SIMD 优化内核和基于 LLVM 的表达式引擎,为 Spark SQL 带来更好的性能。

Native Engine 架构图

内存数据库场景

云原生内存数据库Tair是阿里云国产自研的云原生内存数据库。在完全兼容Redis的基础上,提供了丰富的数据模型和企业级能力来帮助客户构建实时在线场景。Tair广泛应用于政务、金融、制造、医疗和泛互联网等各行业客户,满足客户的高速查询和计算场景。

TairRoaring是基于Tair引擎的Roaring Bitmap实现。Bitmap(又名Bitset)是一种常用的数据结构,使用少量的存储空间来实现海量数据的查询优化。尽管Bitmap相比常规基于Hash结构的实现节省了大量内存空间,但是常规Bitmap对于稀疏场景下的数据存储仍不够友好,因此有了各种压缩Bitmap的实现(Comprised bitmap),Roaring Bitmap就是业界公认的一种更高效和均衡的Bitmap压缩存储的实现。TairRoaring使用了包括SIMD instructions、Vectorization、PopCnt算法等多种工程优化,提升了计算效率,实现了高效的时空效率。通过X-SIMD指令转换,可以大幅度降低Tair在arm上重新通过SIMD实现Bitmap相关算法难度,降低迁移倚天的开发成本。

X-SIMD性能

对比华为Avx2Neon,X-SIMD在接口实现更丰富,接口性能更优。以下是摘取部分ffmpeg中使用的相关API,对比了两者的实现的性能差异,每个接口执行100000次统计其耗时时长,性能提升从5%~138%不等。

接口

avx2neon cycle(us)

x-simd cycle(us)

x-simd 实现指令

接口性能提升

_mm256_mullo_epi32

576

458

NEON

26%

_mm256_mullo_epi16

576

460

NEON

25%

_mm256_storeu_si256

370

326

NEON

13%

_mm256_cvtepi32_ps

350

329

NEON

6%

_mm256_cvtps_epi32

333

254

NEON

31%

_mm256_avg_epu8

569

473

SVE

20%

_mm256_mulhi_epi16

1044

442

SVE

136%

_mm256_mulhi_epu16

1043

439

SVE

138%

_mm256_permute4x64_epi64

1,027

710

SVE

45%

_mm256_extracti128_si256

249

220

SVE

13%

_mm256_inserti128_si256

620

616

SVE

21%

X-SIMD在HPC GKL benchmark中表现性能提升8倍,在GATK端到端测试性能提升20%。

       

GKL benchmark

在大数据Oap中,X-SIMD 完成了Arrow组件的适配,SparkSQL性能端到端提升2~5%。

tpcds sql请求

baseline

x-simd

性能提升

query1

17.23

18.537

7.1%

query2

23.937

28.595

16.3%

query3

14.112

15.269

7.6%

query4

107.661

105.457

-2.1%

query5

32.148

34.798

7.6%

query6

13.119

13.416

2.2%

query7

23.097

23.687

2.5%

query8

13.89

13.806

-0.6%

query9

52.583

50.993

-3.1%

query10

19.611

19.812

1.0%

average

3.80%

综述

X-SIMD是一个modern SIMD指令的开源C库,它提供了一系列丰富的跨平台兼容的函数,可以在aarch64 CPU架构上直接运行x86_64 intrinsic指令,并实现了高效的向量化处理操作,易于移植和扩展。如果你需要对大量数据进行引擎程序设计、数学运算、图像/音视频处理等高性能计算操作,而又希望代码简单易用、具有良好的可移植性和可维护性,并且正打算迁移到aarch64平台上,那么X-SIMD正是你所需要的。X-SIMD可以帮助你降低了向量化代码迁移的门槛,欢迎有需要的伙伴前来咨询。

相关实践学习
基于MaxCompute的热门话题分析
本实验围绕社交用户发布的文章做了详尽的分析,通过分析能得到用户群体年龄分布,性别分布,地理位置分布,以及热门话题的热度。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
相关文章
|
6月前
|
大数据 API 图形学
Unity优化——批处理的优势
Unity优化——批处理的优势
174 0
|
3月前
|
机器学习/深度学习 并行计算 算法
Python中最简单易用的并行加速技巧
Python中最简单易用的并行加速技巧
|
3月前
|
自然语言处理 Java
自研分布式训练框架EPL问题之实现显存的极致优化如何解决
自研分布式训练框架EPL问题之实现显存的极致优化如何解决
|
3月前
|
并行计算 算法 调度
自研分布式训练框架EPL问题之提高GPU利用率如何解决
自研分布式训练框架EPL问题之提高GPU利用率如何解决
|
API Go PHP
推荐一个最好用的高性能、低内存、跨平台的图片处理库
用C++开发的图像处理库,支持的API超过300多个,涵盖了算术、颜色、图片转换、卷积、形态、马赛克、合并、切割等。
608 0
推荐一个最好用的高性能、低内存、跨平台的图片处理库
|
并行计算
《CUDA高性能并行计算》----3.3 标准操作流程
看过以上典型的程序流程,我们来分析一下流程哪些部分是CUDA带来的开销,哪些能带来收益。开销应该是十分明显的:创建镜像数组并且在设备端和主机端传输数据,这些都是在串行计算中不需要进行的额外工作。为了抵消这些内存操作的“额外开销”,我们能从GPU成百上千的处理器核心上得到运算上的收益。
1205 0
|
并行计算 C++
《CUDA高性能并行计算》----0.8 用户指南
我们编写本书的目的就是让广大的技术型读者积极参与到使用CUDA进行GPU并行计算的洪流之中。作为比喻,我们邀请你进行一次基于GPU并行计算的旅程,而本书即是本旅程的导游手册。比喻为导游手册在许多方面都是恰当的,其中包括:
1340 0
|
并行计算 C++ Windows
《CUDA高性能并行计算》----0.7 本书代码
本书应用程序的代码可以通过www.cudaforengineers.com获取。虽然书中采用了一些代码片断和“骨架”代码(skeleton code),但标记为“代码清单”的代码(包含行号)是真实可运行代码的一部分。
2025 0