如果可以优化,是否在Swift中预先计算局部let常数?
我想在Swift 3中编写一些高度优化的通用函数,这些函数适用于任何浮点类型,因此我提出了类似的方法,它可以工作:如果可以优化,是否在Swift中预先计算局部let常数?,swift,optimization,Swift,Optimization,我想在Swift 3中编写一些高度优化的通用函数,这些函数适用于任何浮点类型,因此我提出了类似的方法,它可以工作: @inline(__always) public func radsToDegs<T: BinaryFloatingPoint>(_ angle: T) -> T { let radsToDegsConstant: T = 180/T(M_PI) return angle*radsToDegsConstant } let a: CGFloat =
@inline(__always) public func radsToDegs<T: BinaryFloatingPoint>(_ angle: T) -> T {
let radsToDegsConstant: T = 180/T(M_PI)
return angle*radsToDegsConstant
}
let a: CGFloat = CGFloat(M_PI)
let b = 3.14
let c: Float = 3.14
let d: Double = 3.14
radsToDegs(a) // -> 180.0
radsToDegs(b) // -> 179.9087476710785
radsToDegs(c) // -> 179.9087
radsToDegs(d) // -> 179.9087476710785
但我仍然怀疑Swift编译器是否足够聪明,可以避免在以前的泛型实现中每次调用函数时都计算radsToDegsConstant
这就是我的问题:这是优化的吗?还是有一个技巧,或者一个编译器指令,我仍然可以使用它从泛型函数和扩展表单提供的预计算值中获得好处?我得出结论,在Swift 3中,对泛型函数不能预计算此类常量。以下是我所遵循的路径:我首先尝试了@Leo Dabus在前面的评论中提出的建议(请参见此问题:),并得出了以下代码,但在运行时失败:
private let radsToDegsConstant = 180.0 / .pi
public extension FloatingPoint {
var degsToRads: Self { return self * (radsToDegsConstant as! Self) }
@inline(__always) public func radsToDegs() -> Self {
return self*(radsToDegsConstant as! Self)
}
}
Double.pi.degsToRads
Double.pi.radsToDegs()
Float(M_PI).radsToDegs() // Error: Could not cast value of type 'Swift.Double'to 'Swift.Float'
Float.pi.radsToDegs() // Error: Could not cast value of type 'Swift.Double'to 'Swift.Float'
CGFloat.pi.radsToDegs() // Error: Could not cast value of type 'Swift.Double'to 'CoreGraphics.CGFloat'
这里的问题来自这样一个事实:编译器假定常量radsToDegsConstant为Double,然后强制转换为在运行时失败。还要注意,我尝试了函数和属性(var)语法,只是为了科学
当然,无法将存储的属性添加到扩展,因此以下代码也无效:
public extension FloatingPoint {
let radsToDegsConstant: Self = Self(180.0 / .pi) // Error: extensions may not contain stored properties
var degsToRads: Self { return self * (radsToDegsConstant as! Self) }
@inline(__always) public func radsToDegs() -> Self {
return self*(radsToDegsConstant as! Self)
}
}
注意:按var交换let不会改变任何内容,这是应该的
所以最后我决定从上面提到的链接中获取属性语法,但只关注CGFloat,因为我只需要这个语法尽可能快地工作。下面是优化的扩展:
import CoreGraphics
public let CG_PI = CGFloat(M_PI)
public let CG_PI_2 = CGFloat(M_PI_2)
public let CG_PI_4 = CGFloat(M_PI_4)
public let CG_2PI = CGFloat(2*M_PI)
private let degsToRadsConstant = CG_PI/180 // Optimization: precomputed value.
private let radsToDegsConstant = 180.0/CG_PI // Optimization: precomputed value.
public extension CGFloat {
public var radsToDegs: CGFloat {
@inline(__always) get { return self*radsToDegsConstant }
}
public var degsToRads: CGFloat {
@inline(__always) get { return self*degsToRadsConstant }
}
}
还有几点注意:
- final方法在扩展中不受支持(这可能有点帮助)
- Swift 3中的π:不,不使用这个并不是一个错误,因为在我正在构建的库中,我使用了很多东西,比如m_pi_2和其他π常数与CGFloat。因此,在编译时将它们转换为CGFloat,并使用与M_XX常量提供的命名约定类似的命名约定,在这种特定情况下完全可以。而.pi的问题是,据我所知,Swift库不提供这些其他(我绝对需要的)值。所以为了保持一致性,我一直在使用它,它工作速度很快,类型也很合适。我只是不想从那些敏捷的家伙那里买所有的时尚用品
最后,如果有人仍然用我看不到的技巧来回答这个问题(这允许预先计算常量,并且仍然使用泛型函数,正如我所问的那样),那就太好了,接受新的解决方案作为正确的答案将是一件愉快的事……您可能会对此感兴趣,谢谢,尽管那些链接上的优化程度较低,我喜欢那里的双人敞篷车。但在我看来,使用该实现时,函数将始终使用Double,因此对于较小的浮点类型,效率较低。或者有什么我没有看到的?扩展浮点协议的Swift 3方法使用正确的类型进行操作。我将在操场上进行尝试,并让您知道我是否可以使用该方法预计算值,这是我的目标。谢谢。如果/当Swift支持时,编译器可能会通过为应用它们的每种具体类型专门指定这些常量来进行优化。然而,我不相信目前有一个简单的方法来实现这样的优化在您的情况。
public extension FloatingPoint {
let radsToDegsConstant: Self = Self(180.0 / .pi) // Error: extensions may not contain stored properties
var degsToRads: Self { return self * (radsToDegsConstant as! Self) }
@inline(__always) public func radsToDegs() -> Self {
return self*(radsToDegsConstant as! Self)
}
}
import CoreGraphics
public let CG_PI = CGFloat(M_PI)
public let CG_PI_2 = CGFloat(M_PI_2)
public let CG_PI_4 = CGFloat(M_PI_4)
public let CG_2PI = CGFloat(2*M_PI)
private let degsToRadsConstant = CG_PI/180 // Optimization: precomputed value.
private let radsToDegsConstant = 180.0/CG_PI // Optimization: precomputed value.
public extension CGFloat {
public var radsToDegs: CGFloat {
@inline(__always) get { return self*radsToDegsConstant }
}
public var degsToRads: CGFloat {
@inline(__always) get { return self*degsToRadsConstant }
}
}