为啥有时 Apple Accelerate 框架很慢?

Posted

技术标签:

【中文标题】为啥有时 Apple Accelerate 框架很慢?【英文标题】:Why sometimes Apple Accelerate framework is slow?为什么有时 Apple Accelerate 框架很慢? 【发布时间】:2016-10-08 09:32:42 【问题描述】:

我正在使用来自 Apple 的 vecLib 和 Accelerate framework 作为动态库 + 我在基于 C lang 的项目和 Swift 游乐场中使用 C 和 Swift 3.0 代码。

并且在从 framework 的 SIMD 指令的 framework 调用 Apple 包装器的情况下,具有 1 个或 framework 的 vvcospif() 比简单的标准 @987654324 慢@当函数从循环中调用近 1000 次时为例。

我知道vvcospif()cos() 之间的区别,我应该完全使用vvcospif() 代替x * PI

操场上的例子,你可以复制代码并运行它:

import Cocoa
import Accelerate

func cosine_interpolate(alpha: Float, a: Float, b: Float) -> Float 
    let ft: Float = alpha * 3.1415927;
    let f: Float = (1 - cos(ft)) * 0.5;

    return a + f*(b - a);


var start: Date = NSDate() as Date

var interp: Float;

for index in 0..<1000 
   interp = cosine_interpolate(alpha: 0.25, a: 1.0, b: 0.75)


var end = NSDate();
var timeInterval: Double = end.timeIntervalSince(start);

print("cosine_interpolate in \(timeInterval) seconds")

func fast_cosine_interpolate(alpha: Float, a: Float, b: Float) -> Float 
    var x: Float = alpha
    var count: Int32 = 1

    var result: Float = 0
    vvcospif(&result, &x, &count)

    let SINSIN_HALF_X: Float = (1 - result) * 0.5;

    return a + SINSIN_HALF_X * (b - a);


start = NSDate() as Date

for index in 0..<1000 
    interp = fast_cosine_interpolate(alpha: 0.25, a: 1.0, b: 0.75)


end = NSDate();
timeInterval = end.timeIntervalSince(start);

print("fast_cosine_interpolate in \(timeInterval) seconds")

我的问题是:

为什么vvcospif() 在这个例子中很慢?

可能是因为 vvcospif() 它是 Objective-C 运行时下的包装器,并且从 Intel SIMD 转换数据结构/复制内存 -> Objective-C -> Swift 运行时比小 cos() 慢?

我也有 C 代码的性能问题 +

#include <Accelerate/Accelerate.h>

vvcospif(resultVector, inputVector, &count);

inputVectorresultVector 是具有 1 个或 2 个元素或只是浮点变量的小数组时,循环调用约 1.000.000 次。

cos(x * PI) 计算时间接近 20 毫秒。

vvcospif(x) 处理一个 floatfloat array[2] - 计算时间接近 80 毫秒!加速度在哪里? :)

是的,在 Xcode 中,我使用编译器 -O -whole-module-optimization 优化整个模块选项。已启用。

【问题讨论】:

我假设第一种方法中的a + f(b - a)应该是a + f*(b - a)? – 我在 MacBook 上运行您的代码并得到以下时间:cosine_interpolate:0.74 毫秒,fast_cosine_interpolate:0.1 毫秒。 谢谢,这是带有 f * 动作的代码错误。在我的 mac mini 上: cosine_interpolate 在 0.461017966270447 秒内 fast_cosine_interpolate 在 0.545050024986267 秒内 -o 设置输出文件名。 -O3 -ffast-math 启用全面优化。这是否可以解释您的时间比 Martin 慢 3 个数量级? 不,我用 -O 更新了问题并修复了错误:在 Xcode 中选择了发布和调试的默认优化,这个结果也在默认的 Swift 3.0 和 Xcode 8、Sierra 操场上。在操场“项目”中,我无法选择优化标志... vvcospif 是一个 vForce 函数。 vForce 处理任意长度的向量,这会产生一些开销,因此不建议用于极短的向量。苹果自己recommends:“考虑在超过 16 个元素时使用 vForce”。对于标量使用,请考虑使用__cospif,根据已引用的来源,可从 ios 7 和 OS X 10.9 获得。 【参考方案1】:

有关示例的更详细讨论,请参阅"Introduction to Fast Bezier (and Trying the Accelerate.framework)"。

第一个基本问题是非内联函数调用非常昂贵。如果您可以在性能关键代码中提供帮助,则您不需要函数调用。在一个模块中,编译器通常可以为您内联函数,并且可以为您内联部分标准库。但是当你开始跨越模块障碍时,Swift 通常无法优化调用。

SIMD 函数的意义在于,您可以以正确的格式设置所有数据,然后只调用一次。这样,函数调用的成本由您调用的 SIMD 优化代码组成。

但请记住,您不必调用 Accelerate 即可获得 SIMD 优化。编译器完全能够注意到您已经编写了一个循环并将其转换为内联 SIMD 算法本身(并且它一直都在这样做)。所以对于许多简单的问题,编译器无论如何都会赢。想一想:如果调用 vvcospif 计数为 1 比调用 cos 快,他们不就是这样实现 cos 吗?

我没怎么玩过你的代码,但是如果你想用 Accelerate 来提高它的性能,你想考虑一下如何排列你的所有输入数据,这样你就可以用一个大的 N 调用一次 vvcospif。在这种情况下,很有可能会比循环快得多(因为cos 不是微不足道的)。

如果您想要一个 Accelerate 在实践中的示例,以及您需要如何组织数据,请参阅 PinchText。这段代码基于多达 10 次实时触摸计算包含数千个字形的页面的偏移量,并带有动画(请参阅PinchText.mov 了解结果的样子)。特别注意adjustViewPositions:count:forTouchPoint:。请注意count 的大小,并且数据是逐步转换的,没有循环。即使在该方法中加入一个(非常昂贵的)ObjC 方法调用也无关紧要,因为它只进行了一次。摆脱循环中的函数调用是性能编程的重要组成部分。

【讨论】:

除了回答这个问题之外,您还提出了一个很好的观点,即 Casey Muratori 所谓的“非悲观化”,即使没有显式优化,它也会导致大多数程序员更快地编写代码,即简单地避免多次调用首先是不必要的开销。想象一下,尝试通过完整的函数调用调用 SIMD 指令,一次一个操作数,函数调用 inside 一个循环......

以上是关于为啥有时 Apple Accelerate 框架很慢?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Apple Accelerate 框架的希尔伯特变换(分析信号)?

如何从链接到 Apple Accelerate 框架的源代码构建 NumPy?

iOS Accelerate Framework vImage - 性能改进?

使用 Apple Accelerate 框架选择实数和复数 2D FFT

Apple Accelerate vDSP fft vs DFT 和比例因子

何时在 Apple macOS 上使用 Metal 而不是 Accelerate API