为什么有时 Apple Accelerate 框架很慢?
Why sometimes Apple Accelerate framework is slow?
我正在使用 C 和 Swift 3.0 代码,使用来自 Apple 的 vecLib 和 Accelerate framework 作为动态库 + 我在基于 C lang 的项目中的代码和Swift游乐场。
并且在从 framework 的 SIMD 指令调用 Apple 包装器的情况下,具有 1 或 < 4 个元素的计算函数,例如 vvcospif()
来自 framework[=例如,当函数从循环调用接近 1.000 次时,62=] 比简单标准 cos(x * PI)
慢。
我知道 vvcospif()
和 cos()
之间的区别,我应该使用 vvcospif()
来代替 x * PI
。
playground 中的示例,您可以只复制代码并 运行 它:
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);
当 inputVector
和 resultVector
是具有 1 或 2 个元素或只是 float 变量的小数组,并且循环调用约 1.000.000 次时。
cos(x * PI)
计算时间接近 20 毫秒。
和
vvcospif(x)
处理一个 float
或 float array[2]
- 计算时间接近 80 毫秒!加速在哪里? :)
是的,在 Xcode 中,我使用编译器 -O -whole-module-optimization
优化整个模块选项。已启用。
有关示例的更详细讨论,请参阅 "Introduction to Fast Bezier (and Trying the Accelerate.framework)"。
第一个基本问题是非内联函数调用非常昂贵。如果您可以在性能关键代码中提供帮助,则您不希望函数调用。在模块中,编译器通常可以为您内联函数,并且可以为您内联部分 stdlib。但是当您开始跨越模块障碍时,Swift 通常无法优化调用。
SIMD 函数的要点是您以正确的格式设置所有数据,然后只调用一次。这样,函数调用的成本由您正在调用的 SIMD 优化代码组成。
但请记住,您不必调用 Accelerate 来获得 SIMD 优化。编译器完全能够注意到您编写了一个循环并将其本身变成一个内联 SIMD 算法(并且它一直这样做)。所以对于很多简单的问题,编译器无论如何都会赢。想一想:如果调用计数为 1 的 vvcospif
比调用 cos
更快,他们难道不会那样实现 cos
吗?
我没怎么玩过你的代码,但如果你想用 Accelerate 提高它的性能,你想考虑如何安排你所有的输入数据,这样你就可以调用 vvcospif
一次一个大的 N。在这种情况下,它很可能比循环快得多(因为 cos
不是微不足道的)。
如果您想要 Accelerate 在实践中的示例,以及您需要如何组织数据,请参阅 PinchText. This code is computing offsets for a page full of a few thousand glyphs based on up to 10 touches in real-time, with animations (see PinchText.mov 以了解结果)。特别要看adjustViewPositions:count:forTouchPoint:
。请注意 count
是多么大,并且数据是在没有循环的情况下逐步转换的。即使将(非常昂贵的)ObjC 方法调用投入该方法也无关紧要,因为它只进行了一次。摆脱循环中的函数调用是性能编程的重要组成部分。
我正在使用 C 和 Swift 3.0 代码,使用来自 Apple 的 vecLib 和 Accelerate framework 作为动态库 + 我在基于 C lang 的项目中的代码和Swift游乐场。
并且在从 framework 的 SIMD 指令调用 Apple 包装器的情况下,具有 1 或 < 4 个元素的计算函数,例如 vvcospif()
来自 framework[=例如,当函数从循环调用接近 1.000 次时,62=] 比简单标准 cos(x * PI)
慢。
我知道 vvcospif()
和 cos()
之间的区别,我应该使用 vvcospif()
来代替 x * PI
。
playground 中的示例,您可以只复制代码并 运行 它:
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);
当 inputVector
和 resultVector
是具有 1 或 2 个元素或只是 float 变量的小数组,并且循环调用约 1.000.000 次时。
cos(x * PI)
计算时间接近 20 毫秒。
和
vvcospif(x)
处理一个 float
或 float array[2]
- 计算时间接近 80 毫秒!加速在哪里? :)
是的,在 Xcode 中,我使用编译器 -O -whole-module-optimization
优化整个模块选项。已启用。
有关示例的更详细讨论,请参阅 "Introduction to Fast Bezier (and Trying the Accelerate.framework)"。
第一个基本问题是非内联函数调用非常昂贵。如果您可以在性能关键代码中提供帮助,则您不希望函数调用。在模块中,编译器通常可以为您内联函数,并且可以为您内联部分 stdlib。但是当您开始跨越模块障碍时,Swift 通常无法优化调用。
SIMD 函数的要点是您以正确的格式设置所有数据,然后只调用一次。这样,函数调用的成本由您正在调用的 SIMD 优化代码组成。
但请记住,您不必调用 Accelerate 来获得 SIMD 优化。编译器完全能够注意到您编写了一个循环并将其本身变成一个内联 SIMD 算法(并且它一直这样做)。所以对于很多简单的问题,编译器无论如何都会赢。想一想:如果调用计数为 1 的 vvcospif
比调用 cos
更快,他们难道不会那样实现 cos
吗?
我没怎么玩过你的代码,但如果你想用 Accelerate 提高它的性能,你想考虑如何安排你所有的输入数据,这样你就可以调用 vvcospif
一次一个大的 N。在这种情况下,它很可能比循环快得多(因为 cos
不是微不足道的)。
如果您想要 Accelerate 在实践中的示例,以及您需要如何组织数据,请参阅 PinchText. This code is computing offsets for a page full of a few thousand glyphs based on up to 10 touches in real-time, with animations (see PinchText.mov 以了解结果)。特别要看adjustViewPositions:count:forTouchPoint:
。请注意 count
是多么大,并且数据是在没有循环的情况下逐步转换的。即使将(非常昂贵的)ObjC 方法调用投入该方法也无关紧要,因为它只进行了一次。摆脱循环中的函数调用是性能编程的重要组成部分。