前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++那些事之高性能SIMD

C++那些事之高性能SIMD

作者头像
公众号guangcity
发布2023-09-02 10:42:44
5890
发布2023-09-02 10:42:44
举报
文章被收录于专栏:光城(guangcity)光城(guangcity)

C++那些事之高性能SIMD

最近在看相关向量化的内容,看起来有点头大,借此机会,学习一下高性能SIMD编程。

SIMD全称single-instruction multiple-data,单指令多数据。

在传统的计算机架构中,CPU一次只能处理一个数据元素。但是,许多任务涉及对大量数据执行相同的操作,例如对数组中的所有元素进行加法、乘法或逻辑操作等。SIMD编程通过向CPU提供专门的指令集,使得CPU能够同时对多个数据元素执行相同的操作。

这种处理方式特别适合涉及向量、矩阵、图像、音频和视频等数据的计算。

目前比较常用的有SSE、SSE2、AVX128、AVX256、AVX512。

本节,将简单学习一下AVX512的一些操作,操作比较多,这里只是引入一些。

1.术语

首先第一个问题便是,simd编程的代码跟平时写的代码长相不大一样,各种下划线以及命名,完全看不懂,如何理解呢?

诸如:

  • _mm512_set1_ps

Broadcast single-precision (32-bit) floating-point value a to all elements of dst.

  • _mm512_set1_epi32

Broadcast 32-bit integer a to all elements of dst.

于是,找到了下面这个表格:

Abbreviation

Full Name

C/C++ Equivalent

ps

packed single-precision

float

ph

packed half-precision

None*

pd

packed double-precision

double

pch

packed half-precision complex

None*

pi8

packed 8-bit integer

int8_t

pi16

packed 16-bit integer

int16_t

pi32

packed 32-bit integer

int32_t

epi8

extended packed 8-bit integer

int8_t

epi16

extended packed 16-bit integer

int16_t

epi32

extended packed 32-bit integer

int32_t

epi64

extended packed 64-bit integer

int64_t

epi64x

extended packed 64-bit integer

int64_t

https://stackoverflow.com/questions/70911872/what-are-the-names-and-meanings-of-the-intrinsic-vector-element-types-like-epi6

再比如:

_mm512_mask_load_ps

_mm512_mask_loadu_ps

u表示unordered,表示加载无序,当使用 _mm512_mask_loadu_ps 函数加载内存中的数据时,不会执行对内存地址的任何对齐要求。而_mm512_mask_load_ps要求满足 64 字节对齐要求。

这样对照着学习,非常快的便可以知道每个接口的含义了。

相关API可以看看Intel Intrinsics Guide。

https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html

2.实际例子

2.1 求最小值

对于512位,我们可以存储16个32位float。

代码语言:javascript
复制
static inline rf_512 load(float* ptr) { return _mm512_loadu_ps(ptr); }
float a[width] = {1.0, 2.0,  3.0,  22.0, 5.0,  17.0, 9.0,  8.0,
                  9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0};
float b[width] = {3.3, 6.2,   5.3,   4.4,   5.5,   6.6,   7.7,  8.8,
                  9.9, 10.10, 21.11, 12.12, 13.13, 14.14, 15.0, 16.16};
rf_512 data = minimum(load(a), load(b));

于是我们可以快速得到:

代码语言:javascript
复制
1 2 3 4.4 5 6.6 7.7 8 9 10 11 12 13 14 15 16

2.2 快速打乱数据顺序

对于输入数据是

代码语言:javascript
复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

我们可以快速得到:

代码语言:javascript
复制
16.16 2 14.14 13.13 5 6 7 8 9 10 11 12 13 14 15 16

对应实现:

代码语言:javascript
复制
static inline rf_512 permutexvar(ri_512 idx, rf_512 src) {
  return _mm512_permutexvar_ps(idx, src);
}
/*
raw data: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
shuffle data: 16.16 2 14.14 13.13 5 6 7 8 9 10 11 12 13 14 15 16
*/
void print_permutexvar_mask() {
  float a[width] = {1.0, 2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,
                    9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0};
  float b[width] = {1.1, 2.2,   3.3,   4.4,   5.5,   6.6,   7.7,  8.8,
                    9.9, 10.10, 11.11, 12.12, 13.13, 14.14, 15.0, 16.16};
  mask_type mask = make_bit_mask<1, 0, 1, 1, 0>();
  int idx_array[width] = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
  ri_512 idx = _mm512_loadu_si512((__m512i*)idx_array);
  rf_512 result = permutexvar_mask(load(a), mask, idx, load(b));
  float result_arr[width];
  store(result, result_arr);
  std::cout << "raw data: ";
  for (int i = 0; i < width; i++) {
    std::cout << a[i] << " ";
  }
  std::cout << std::endl;

  std::cout << "shuffle data: ";
  for (int i = 0; i < width; i++) {
    std::cout << result_arr[i] << " ";
  }
  std::cout << std::endl;
}

2.3 旋转

对于一个数组:

代码语言:javascript
复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

我们可以旋转得到:

代码语言:javascript
复制
4 5 6 7 8 9 10 11 12 13 14 15 16 1 2 3

or

代码语言:javascript
复制
14 15 16 1 2 3 4 5 6 7 8 9 10 11 12 13

也就是rotate down or rotate up。

在这里,我们可以这样实现:

代码语言:javascript
复制
rf_512 rotateGeneral(float* arr, int s) {
  int idx_array[width];
  for (int i = 0; i < width; i++) {
    idx_array[i] = (i + s) % width;
  }
  ri_512 idx = _mm512_loadu_si512((__m512i*)idx_array);
  rf_512 result = permutexvar(idx, load(arr));
  return result;
}

等等,还有其他的例子,可以发现通过使用simd,我们可以实现一些非常有趣的算法,加速对数组,批量数据的处理。

后面会继续学习simd,一起加油吧~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-07-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光城 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C++那些事之高性能SIMD
    • 1.术语
      • 2.实际例子
        • 2.1 求最小值
      • 2.2 快速打乱数据顺序
        • 2.3 旋转
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档