并行编程simd_单核并行编程:SIMD组件性能提升和基准测试

并行编程simd

当事情变得如此复杂时,您通常知道这是英特尔的错。 来源: Pixabay

SIMD汇编指令使您可以在单个内核中并行处理大量数据。 我已经说了一次 ,我会再说一遍:编程是我们最不可思议的事情。

Shell命令就像小型Cantrip,Python脚本对Tulpas几乎没有帮助。 我们甚至有自己的守护程序! 但是,每当我们需要将性能压缩到最后一个字节时–当我们知道一个错误的步伐都会使程序速度大大降低时…那是最黑暗的黑魔术师Assembly出现的时候。

由于您可能没有注意到,我最近没写太多(顺便说一句,如果这是您第一次阅读我,欢迎光临!很高兴见到您!)。 那是因为上周我进行了非常严苛的考试,并且我不得不做很多准备。 该主题称为“计算机组织II”,与时俱进是一个巨大的挑战。
因此,我决定参加练习时做的其中一项练习,然后将其变成一篇文章。 这样一来,我可以用一块石头砸掉两把剪刀(杀死鸟是不好的,你应该感到很难过)。

在不拖延的情况下,让我们直接进行讨论。 和往常一样,此Github项目中提供了代码。

什么是处理器指令?

我们编写的所有代码(无论是用Python,Java还是C语言编写)最终都将被解释或编译为我们的CPU的微型原子指令(从程序员的角度)。
这些指令数以千计,它们中的每一个都做很小的事情,直接与硬件进行交互。

例如,一条指令可以将一个值写入内存(变量赋值转换为该值),将其打开或关闭或进行逻辑

我的PC上装有Intel处理器,这也是我们在课堂上学习到的体系结构,因此,对所有使用阅读器的ARM感到抱歉,今天我的理解力不足。

编写这些指令的语言(将1:1转换为文字二进制)称为汇编语言。

将C转换为汇编:让我们成为编译器已有一段时间。

在本文中,我们将使用非常小的C函数。 这是完整的代码:

该函数将指向字节流的指针(一个char重一个字节),一个 char和一个want char作为参数,并假设该流以0结尾(如果不是,则崩溃为分段错误)的情况),然后逐字节进行迭代,将每个“ have”实例替换为want。就C语言而言,它的速度与它一样快-并且远比Python快(当我运行一些基准测试时) ,此函数的Python版本花了两分钟的时间来完成输入(在C语言中花了6秒)。

经过编译器后,此功能在汇编语言中是什么样的? 可能是这样的:

运行该汇编函数而不是C版本不应提高我们的性能。 由于编译器知道一些我们可能不知道的技巧,并且对这种代码进行了一些优化,因此它甚至可能降低它的效率。

有一种通常不会使用的优化,但是当它使用时,它永远不会充分利用它。

SIMD指令:单指令,多数据

每当我们想到并行性时,我们就会想到多核进程,甚至集群。 但是,如果我们使一个核心一次完成很多事情,该怎么办? 这就是英特尔几十年前的想法,从那时起图像处理的世界就不一样了。

您会看到,通常数据存储在我们的CPU中,就像我们刚刚使用的那些一样,存储在通用寄存器中。 它们中的大多数为64位,因此可以存储long, floatint。 好吧,从技术上讲,是两个int ,但是仍然不足以值得并行使用它们的说明。

但是,大多数处理器具有更大的可用寄存器: XMM ,其上具有128位。 这足以容纳16个完整字节!

我们开始这个派对吧。 来源: Pixabay

想象一下,如果一次可以处理16个字节,并使我们的程序快16倍,该怎么办。 从内存中读取一次并获取16个不同的字节? 检查一下 批量处理它们并将它们一次又一次写入内存中? 检查一下 可能性是无止境。

尤其是在图像或信号处理中,这为更快地进行并发计算打开了可能性。 就像,整个数量级更快。 您知道平凡的并发功能是什么? 处理字节流,它们之间没有依赖性。

不过,有一个陷阱:如果常规的Intel指令对您而言似乎违反直觉或丑陋,请准备好SIMD。 他们的名字令人目结舌,而且我们大多数人都不能连续写出其中5个,除非在官方手册中查找它们( 可免费使用 )。

处理了所有这些警告之后,让我指导您完成刚才显示的功能的SIMD实现。
让我警告您:它虽然不漂亮,但是却非常快。

第一个示例:使用SIMD汇编指令获取字符串的长度。

首先,我对流进行一次迭代以将其放大。 为简单起见,此函数假定流的大小可被16整除(末尾加零,至少有一个0字节表示流的末尾),因为否则我只需要添加一个新的if并运行size函数的非SIMD版本。
假设我们的用户愿意在将阵列传递给我们之前对其进行填充以使其可分割,以换取性能提升和节省的治疗时间。

如果您需要呕吐或洗个澡,我会明白的。 收藏这篇文章,一个小时后再回来。
但是,我们每16个字节从内存读取一次。
这使得该程序比C版本快16倍!

现在,在第二部分中,让我们实际执行要求的操作:让我们替换一些字节!

而已。 由于布尔操作是逐字节进行的,因此比较也是并发的,因此我们实际上可以完全并行地处理每组16个字节。

免责声明:对于那些真正关心性能的人,请注意,我已经进行了2次内存读取,每次都读取相同的数据:一次计算长度,然后进行另一次替换。

一次完成所有操作将是最佳选择,但是代码将更加丑陋而不是那么具有教育意义。 这就是我们希望该程序比C版本仅快8倍而不是16倍的原因。它仍然是一个非常不错的改进。

使用SIMD的速度有多快? 基准!

为了运行这些基准测试,我仅在一个小程序(可从Github项目获得 )中使用time.h C库。 我所做的就是:

  • 选择一个数组大小(16的倍数)
  • 使用给定大小的交替值初始化字节数组
  • 运行该函数的C版本,并测量完成循环所需的时间。
  • 对SIMD组装版本执行相同的操作。

我用1.6e7、1.6e8和1.6e9的输入大小重复了这些步骤。 我停在那里,是因为下一站所花的时间比我希望等待的时间长一点,但是趋势非常明显:

INPUT_SIZE | ASM  | C (Seconds)
1.6e7 | 0.005 | 0.069
1.6e8 | 0.050 | 0.687
1.6e9 | 0.466 | 6.868

它比我预期的还要线性! 该比率约为13,因此它甚至比我估计的还要好。 我想这与减少内存读取有关,但是请随时在注释中保留自己的假设。

结论

我的第一个结论是,学习汇编很有趣。 我的第二个结论是,我无法通过该考试。

出于实用性考虑,我的第三个结论是编写SIMD程序非常有效,只要我们需要更快地执行某些操作,我们都可以尝试使用它。

或者,如果您更喜欢高级语言,则可以寻找在其实现中已经使用SIMD指令的框架(咳嗽,NumPy,Pandas)。

无论如何,我认为了解低级内容和处理器的内部工作原理可以帮助我们编写更好的代码,并更好地了解事物在后台的运行方式。

今天就这些。 我希望您发现这篇文章很有趣,甚至有用。 如果您想进一步阅读本主题,建议您考虑一个可能对并行处理有用的问题,尝试使用SIMD编写它,然后每隔两个步骤查阅Intel手册以了解新的说明。

像往常一样,如果您发现我的代码中有任何错误,或者我可以进一步优化此错误的方法,甚至是错字,请在评论中告诉我! 对于任何积极的反馈也是如此,这一直是人们所赞赏的。

我待会见,继续编码!

Twitter Medium 上关注我, 以继续接收更多文章和教程。 请在您使用的任何社交媒体上分享此文章。
也许您有一个最近打算了解更多组装知识的朋友? 用这个打他!

最初于 2018年10月6日 www.dataden.tech 发布

翻译自: https://hackernoon.com/harnessing-the-power-of-simd-sse-assembly-instructions-for-good-fdaa8ce34e9a

并行编程simd

评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符 “速评一下”
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页