c++26新功能—SIMD

一、弗林分类法

在计算机的体系分类中,有一种叫做弗林(Flynn’s Taxonomy)分类法,即基于指令流和数据流数量进行分类的方法。按照弗林分类法,分为以下几类:
1、SISD-单指令流单数据流
指令部件仅处理一条指令,数据流由单一操作部件操作。典型代表为传统单核处理器,比如早期的电脑中的CPU都是如此
2、SIMD-单指令流多数据流
单一指令流控制多个处理单元同时处理不同数据。适用于向量化并行处理,这个最典型的就是GPU的计算处理
3、MISD-多指令流单数据流
多个指令流处理同一数据流。这个基本没有实践价值,应用极少,一般在理论验证场景
4、MIMD-多指令流多数据流
多个独立处理单元分别执行不同指令并处理不同数据。代表现代多核处理器、分布式系统及集群计算

弗林分类法主要用于描述计算机系统的并发处理能力,随着技术发展,MIMD已成为现代计算系统的主流架构(主要是多核和分布式的需求)。

二、SIMD

在上面说明了弗林分类的基本情况,其实弗林分类法重点就是解决指令流和数据流的对应处理方式。而在实际的应用中,目前GPU和并行编程技术的快速发展,使得SIMD变得比较突出。SIMD,Single Instruction, Multiple Data,指令即单指令多数据流。它可以上CPU一个时钟周期内对多个数据执行相同的运算操作,从而提升处理效率。主要特点是:
1、SIMD利用一条指令对存储的多个数据进行处理,这些数据一般存储在宽寄存器(128位、256位或512位)中
2、SIMD指令由Intel推出从MMX到SSE、AVX和AVX-512等,这些指令集支持不同宽度的寄存器和操作类型(这个很重要)
3、C++26中的std::simd特性可以最大化的利用SIMD指令进行向量化编程,从而提高代码的效率和性能以及代码的可移植性
4、SIMD本身就能更好的支持并行处理数据,提高效率。典型的就是科学计算和图形、视频等的处理

在C++26中为了适应并行编程技术的发展,推出了std::simd数据并行类型库。按文档中说的“该库提供了数据并行类型以及对这些类型的操作:可移植类型,用于显式声明数据并行性,并通过数据并行执行资源(如 SIMD 寄存器和指令或由通用指令解码器驱动的执行单元)来构建数据。”

二、std::simd的应用

std::simd主要用于支持数据并行计算,也就是可以更方便的进行向量化操作,充分利用CPU的SIMD指令加速数据处理。从而更好的发挥软硬件协调的效率。另外使用此库可以避免原来的开发者直接操作硬件的指令,让相关的代码更加容易理解和移植。
std::simd的主要功能包括:
1、向量化类型std::simd:提供类似数组的数据类型且支持SIMD的并行操作,允许在多个数据上并行进行算术操作,支持更广泛的标题类型并可以与底层硬件自动适配向量宽度
2、自动向量化:自动选择最佳的向量化指令集,从而让相同的代码在不同的硬件平台可以达到最好的适应性,加强了可移植性
3、数据控制:提供多种数据的加载和对齐存储方式,提供了灵活的内存布局处理方式
4、掩码操作:提供了用于条件控制的布尔掩码,可以在更细节的场景下对元素进行操作
5、兼容性:提供了更好的兼容性(比如对标准库的容器兼容),便于维护和调试
std::simd的主要应用场景有:
1、图形和视频的处理:比如视频的编解码,图像的像素控制、旋转等
2、信号处理:实现各种滤波器和算法如FFT
3、科学计算 :比如常见的矩阵和向量运算,卷积计算等
4、游戏开发:各种引擎中的向量运算和各种复杂场景的实现
5、AI方向:常见的卷积运算、矩阵乘法以及各种随机处理运算等
虽然说std::simd看上去很强大,但实际上它仍然有不小的问题,比如对硬件和编译器的依赖,这都意味着其不可能在短期内快速的广泛应用。

三、例程

看一下std::simd相关的例程:

#include <iostream>
#include <simd>
#include <string_view>
 
void println(std::string_view name, auto const& a)
{
    std::cout << name << ": ";
    for (std::size_t i{}; i != a.size(); ++i)
        std::cout << a[i] << ' ';
    std::cout << '\n';
}
 
template<class A>
constexpr std::basic_simd<int, A> my_abs(std::basic_simd<int, A> x)
{
    return std::simd_select(x < 0, -x, x);
}
 
int main()
{
    constexpr std::simd<int> a = 1;
    println("a", a);
 
    constexpr std::simd<int> b([](int i) { return i - 2; });
    println("b", b);
 
    constexpr auto c = a + b;
    println("c", c);
 
    constexpr auto d = my_abs(c);
    println("d", d);
 
    constexpr auto e = d * d;
    println("e", e);
 
    constexpr auto inner_product = std::reduce(e);
    std::cout << "inner product: " << inner_product << '\n';
 
    constexpr std::simd<double, 16> x([](int i) { return i; });
    println("x", x);
    // overloaded math functions are defined in <simd>
    println("cos²(x) + sin²(x)", std::pow(std::cos(x), 2) + std::pow(std::sin(x), 2));
}

执行结果可能是:

a: 1 1 1 1 
b: -2 -1 0 1 
c: -1 0 1 2 
d: 1 0 1 2 
e: 1 0 1 4 
inner product: 6
x: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 
cos²(x) + sin²(x): 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

std::simd的更广泛的应用还需要C++26标准和相关辅助工具进一步协调以及相关实际应用的实践,说白了,实践才是检验真理的最好的方法。

四、总结

std::simd可以认为是C++对并行编程语言的一种对齐努力方式。至少在当今,大多的开发者都已经明白了,如果能从语言本身就支持并行编程,那么开发者的学习和应用成本会大幅的降低。但作为一种老语言与新应用的冲突,不可能在一两个标准演进中就搞定。更何况,并行编程如今也不是多么成熟,不少的新技术和新思想在不断的涌现出来。所以,大家只要认真跟进即可,不要有多少焦虑!

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值