Я хотел бы написать некоторые высоко оптимизированные общие функции в Swift 3, которые работают на любом типе с плавающей точкой, так что я вышел с чем-то вроде этого, который работает:Являются ли локальные константы константы предварительно вычисляемыми в Swift, если они явно могут быть оптимизированы?
@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
Но я хотел бы иметь radsToDegsConstant предварительно вычисленное, и следующий код не компилировать по уважительной причине: это связано с тем, что radsToDegs является родовым:
// error: type 'OptimizedConstants' cannot be nested in generic function 'radsToDegs'
@inline(__always) public func radsToDegs<T: BinaryFloatingPoint>(_ angle: T) -> T {
struct OptimizedConstants {
static let radsToDegsConstant: T = 180/T(M_PI)
}
return angle*OptimizedConstants.radsToDegsConstant
}
Таким образом, мой единственный оставшийся раствор, чтобы не идти на родовой функции, но вместо того, чтобы расширить только тип, который, как я знаю, меня больше всего интересует, и объявить вычисленную константу вне функция:
private let radsToDegsConstant = 180/CGFloat(M_PI)
public extension CGFloat {
@inline(__always) public func radsToDegs() -> CGFloat {
return self*radsToDegsConstant
}
}
let x: CGFloat = CGFloat(M_PI)
x.radsToDegs() // 180.0
Но я до сих пор удивляюсь, если Swift компилятор будет достаточно умен, чтобы избежать вычислений radsToDegsConstant каждый раз, когда функция это называется, на бывшей родовой реализации.
Так что это мой вопрос: это оптимизировано? Или есть трюк или директива компилятора, которую я могу использовать для получения обеих преимуществ от общей функции и предварительно вычисленное значение, которое предоставляет форма расширения?
вам может быть интересно это http://stackoverflow.com/a/29179878/2303865 –
Спасибо, хотя те, на этой ссылке, менее оптимизированы, мне нравится трюк там с DoubleConvertible. Но мне приходит в голову, что с этой реализацией функция всегда будет использовать Double, поэтому эффективность меньше для более мелких типов с плавающей запятой. Или что-то я не вижу? – nbloqs
Метод Swift 3, который расширяет протокол FloatingPoint, использует правильный тип –