0

0

性能追求第四部分:SIMD 力量与您同在

PHPz

PHPz

发布时间:2024-08-07 11:40:40

|

978人浏览过

|

来源于dev.to

转载

性能追求第四部分:simd 力量与您同在

此时,人们可能想知道 numba(围绕 numpy python 代码的 python 编译器)如何提供比 numpy 更高的性能。为此,让我们单独检查所有三角函数的时序(是的,如果您还记得高中的复杂分析课程,指数和对数就是三角函数!)。但该测试不仅仅与那些想要做高中三角学的人相关:物理引擎,例如游戏中会用到这些函数,机器学习和统计计算大量使用log和exp。因此,正确使用三角函数是实现各种应用的一小步,但却是重要的一步。下表显示了 50m 就地转换的时间:

功能 图书馆 执行时间
平方根 麻木 1.02e-01秒
日志 麻木 2.82e-01 秒
经验 麻木 3.00e-01 秒
cos 麻木 3.55e-01秒
罪恶 麻木 4.83e-01 秒
罪恶 麻木 1.05e-01秒
平方根 麻木 1.05e-01秒
经验 麻木 1.27e-01秒
日志 麻木 1.47e-01 秒
cos 麻木 1.82e-01 秒

该表首先体现了性能优势:平方根,一个具有专用 simd 向量化指令的函数,在 numba 和 numpy 中执行的时间完全相同,而所有其他函数的速度都加快了 2 -2.5 时间,表示代码使用 simd 或自动线程自动矢量化。第二条线索是通过使用 ulp(最后一位单位)检查 numba 和 numpy 之间结果的差异来提供的。 ulp 是数值计算准确性的衡量标准,可以使用以下 python 函数轻松计算 numpy 数组:

def compute_ulp_error(array1, array2):
## maxulp set up to a very high number to avoid throwing an exception in the code
    return np.testing.assert_array_max_ulp(array1, array2, maxulp=100000)

这些数值基准表明,平方根函数在 numpy 和 numba 中使用几乎等效的代码,而对于所有其他三角函数,所有 50m 数字的平均值、中值、第 99.9 个百分位数和最大 ulp 值都不同。这是 simd 发挥作用的微妙暗示:矢量化稍微改变了浮点代码的语义以利用关联数学,而浮点数值运算不是关联的。

功能 平均 ulp 中位 ulp 99.9th ulp 最大 ulp
平方根 0.00e+00 0.00e+00 0.00e+00 0.00e+00
罪恶 1.56e-03 0.00e+00 1.00e+00 1.00e+00
cos 1.43e-03 0.00e+00 1.00e+00 1.00e+00
经验 5.47e-03 0.00e+00 1.00e+00 2.00e+00
日志 1.09e-02 0.00e+00 2.00e+00 3.00e+00

最后,我们可以使用下面的代码检查 numba 生成函数的代码以获取矢量化汇编指令,如此处详述:

@njit(nogil=true, fastmath=false, cache=true)
def compute_sqrt_with_numba(array):
    np.sqrt(array, array)


@njit(nogil=true, fastmath=false, cache=true)
def compute_sin_with_numba(array):
    np.sin(array, array)


@njit(nogil=true, fastmath=false, cache=true)
def compute_cos_with_numba(array):
    np.cos(array, array)


@njit(nogil=true, fastmath=false, cache=true)
def compute_exp_with_numba(array):
    np.exp(array, array)


@njit(nogil=true, fastmath=false, cache=true)
def compute_log_with_numba(array):
    np.log(array, array)

## check for vectorization
## code lifted from https://tbetcke.github.io/hpc_lecture_notes/simd.html
def find_instr(func, keyword, sig, limit=5):
    count = 0
    for l in func.inspect_asm(func.signatures[sig]).split("\n"):
        if keyword in l:
            count += 1
            print(l)
            if count >= limit:
                break
    if count == 0:
        print("no instructions found")

# compile the function to avoid the overhead of the first call
compute_sqrt_with_numba(np.array([np.random.rand() for _ in range(1, 6)]))
compute_sin_with_numba(np.array([np.random.rand() for _ in range(1, 6)]))
compute_exp_with_numba(np.array([np.random.rand() for _ in range(1, 6)]))
compute_cos_with_numba(np.array([np.random.rand() for _ in range(1, 6)]))
compute_exp_with_numba(np.array([np.random.rand() for _ in range(1, 6)]))

这就是我们如何探测 vmovups 指令是否存在,表明 ymm avx2 寄存器正在用于计算

print("sqrt")
find_instr(compute_sqrt_with_numba, keyword="vsqrtsd", sig=0)
print("\n\n")
print("sin")
find_instr(compute_sin_with_numba, keyword="vmovups", sig=0)

正如我们在下面的输出中看到的,平方根函数使用 x86 simd 矢量化平方根指令 vsqrtsd ,而 sin(以及所有三角函数)使用 simd 指令来访问内存。

sqrt
        vsqrtsd %xmm0, %xmm0, %xmm0
        vsqrtsd %xmm0, %xmm0, %xmm0


sin
        vmovups (%r12,%rsi,8), %ymm0
        vmovups 32(%r12,%rsi,8), %ymm8
        vmovups 64(%r12,%rsi,8), %ymm9
        vmovups 96(%r12,%rsi,8), %ymm10
        vmovups %ymm11, (%r12,%rsi,8)

现在让我们把注意力转移到 perl 和 c 上(毕竟这是一个 perl 博客!)。在第一部分
我们看到,在评估嵌套函数 cos(sin(sqrt(x))) 时,pdl 和 c 给出了相似的性能,并且在第二部分中,单线程 perl 代码与 numba 一样快。但是,如果没有 inline 模块,pdl 和 c 中的各个三角函数又如何呢?在这种情况下将被评估的 c 代码块是:

#pragma omp for simd
  for (int i = 0; i < array_size; i++) {
    double x = array[i];
    array[i] = foo(x);
  }

其中 foo 是 sqrt、sin、cos、log、exp 之一。我们将使用 simd omp pragma 与以下编译标志和 gcc 编译器结合使用,看看我们是否可以让代码拾取提示并自动向量化使用 simd 指令生成的机器代码。

cc = gcc
cflags = -o3 -ftree-vectorize  -march=native -mtune=native -wall -std=gnu11 -fopenmp -fstrict-aliasing -fopt-info-vec-optimized -fopt-info-vec-missed
ldflags = -fpie -fopenmp 
libs =  -lm

在编译过程中,gcc 会通知我们所有错过的优化循环的绝佳机会。下面的性能表也证明了这一点;请注意,标准 c 实现和 pdl 与 numba 等效且性能相同。 perl 通过 pdl 可以在我们的数据科学世界中提供性能。

功能 图书馆 执行时间
平方根 pdl 1.11e-01 秒
日志 pdl 2.73e-01秒
经验 pdl 3.10e-01秒
cos pdl 3.54e-01秒
罪恶 pdl 4.75e-01 秒
平方根 c 1.23e-01 秒
日志 c 2.87e-01秒
经验 c 3.19e-01秒
cos c 3.57e-01秒
罪恶 c 4.96e-01 秒

为了让编译器使用 simd,我们将标志 -o3 替换为 -ofast,这样就不再错过所有这些绝佳的性能机会,并且 c 代码现在可以提供(但有适用于 -ofast 标志的常见警告) 。

WowTo
WowTo

用AI建立视频知识库

下载
功能 图书馆 执行时间
平方根 c - 奥法斯特 1.00e-01 秒
罪恶 c - 奥法斯特 9.89e-02 秒
cos c - 奥法斯特 1.05e-01秒
经验 c - 奥法斯特 8.40e-02 秒
日志 c - 奥法斯特 1.04e-01秒

通过这些基准测试,让我们回到最初的 perl 基准测试,并对比通过内联 c 代码的非 simd 感知调用获得的时序:

use inline (
    c    => 'data',
    build_noisy => 1,
    with => qw/alien::openmp/,
    optimize => '-o3 -march=native -mtune=native',
    libs => '-lm'
);

以及 simd 感知型(在下面的代码中,必须包含数学库的矢量化版本才能编译代码):

use inline (
    c    => 'data',
    build_noisy => 1,
    with => qw/alien::openmp/,
    optimize => '-ofast -march=native -mtune=native',
    libs => '-lmvec'
);

代码的非矢量化版本产生下表

inplace  in  base python took 11.9 seconds
inplace  in pythonjoblib took 4.42 seconds
inplace  in         perl took 2.88 seconds
inplace  in perl/mapcseq took 1.60 seconds
inplace  in    perl/mapc took 1.50 seconds
c array  in            c took 1.42 seconds
vector   in       base r took 1.30 seconds
c array  in   perl/c/seq took 1.17 seconds
inplace  in     pdl - st took 0.94 seconds
inplace in  python numpy took 0.93 seconds
inplace  in python numba took 0.49 seconds
inplace  in   perl/c/omp took 0.24 seconds
c array  in   c with omp took 0.22 seconds
c array  in    c/omp/seq took 0.18 seconds
inplace  in     pdl - mt took 0.16 seconds

而矢量化的这个:

Inplace  in  base Python took 11.9 seconds
Inplace  in PythonJoblib took 4.42 seconds
Inplace  in         Perl took 2.94 seconds
Inplace  in Perl/mapCseq took 1.59 seconds
Inplace  in    Perl/mapC took 1.48 seconds
Vector   in       Base R took 1.30 seconds
Inplace  in     PDL - ST took 0.96 seconds
Inplace in  Python Numpy took 0.93 seconds
Inplace  in Python Numba took 0.49 seconds
C array  in   Perl/C/seq took 0.30 seconds
C array  in            C took 0.26 seconds
Inplace  in   Perl/C/OMP took 0.24 seconds
C array  in   C with OMP took 0.23 seconds
C array  in    C/OMP/seq took 0.19 seconds
Inplace  in     PDL - MT took 0.17 seconds

为了便于与 python 和 r 的各种风格进行比较,我们将之前提供的结果插入到这两个表中。

带回家的要点(其中一些可能有点令人惊讶):

  1. 基础 python 的性能非常糟糕 - 比基础 perl 慢了近 4 倍。即使在并行处理(joblib)下,8 线程 python 代码也比 perl 慢
  2. r 在考虑的 3 种动态类型语言中表现最好:比基本 python 快近 9 倍,比基本 perl 快 2.3 倍。
  3. 通过访问 c (perl/mapc) 中的数组容器对 perl 数组进行就地修改,缩小了 perl 和 r 之间的差距
  4. 考虑到时序的可变性,三个代码、base r、perl/mapc 和 c 数组的等效就地修改大致相同
  5. 单线程 pdl 代码提供与 numpy 相同的性能
  6. simd 感知代码,例如通过 numba 或(对于本机 c 代码编译器编译指示的情况,例如向量化和非向量化版本的表之间的 c 时序中的 cntract c 数组)提供了最佳的单线程性能。
  7. openmp pragmas 确实可以加速 perl 容器的操作(例如映射)。
  8. 添加线程(omp 代码)或多线程版本的 pdl 可提供最佳性能。

这些观察产生了以下大问题:

  • 观察 1 和 2 令人惊讶的是,基础 python 赢得了数据科学语言的声誉。事实上,凭​​借相当简单的数字代码的这些性能特征,人们应该在数据分析代码中用基本 python 来替代 r(或者对于我们这些使用 perl 的人来说)。
  • 由于涉及 c 和 perl 的混合实现可以使用 simd 感知代码提供真正的性能优势,即使使用单个线程,我们是否应该升级 inline::c 代码以使用 simd 友好的编译器标志?或者(对于那些害怕-ofast的人来说,也许是精心准备的内在函数混合学,甚至是汇编?
  • 我们是否应该考虑升级 perl 的各种 c/xs 模块,以便它们使用 openmp?
  • 为什么没有更多的人使用 pdl 软件这一瑰宝?仅自动线程属性就应该让人们考虑将其用于要求苛刻的数据密集型任务。
  • 是否可以创建依赖 pdl 和 openmp/inline::c 进行不同计算的混合应用程序?
  • 我们是否应该研究 pdl 的特殊构建,利用 simd 属性和编译器编译指令来允许用户鱼与熊掌兼得(simd 矢量化)和吃掉它(自动线程)?

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

769

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

661

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

764

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

639

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1305

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

549

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

579

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

709

2023.08.11

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

19

2026.01.20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 8.1万人学习

Django 教程
Django 教程

共28课时 | 3.3万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号