2017-01-11 1 views
2

Я хотел бы написать некоторые высоко оптимизированные общие функции в 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 каждый раз, когда функция это называется, на бывшей родовой реализации.

Так что это мой вопрос: это оптимизировано? Или есть трюк или директива компилятора, которую я могу использовать для получения обеих преимуществ от общей функции и предварительно вычисленное значение, которое предоставляет форма расширения?

+1

вам может быть интересно это http://stackoverflow.com/a/29179878/2303865 –

+0

Спасибо, хотя те, на этой ссылке, менее оптимизированы, мне нравится трюк там с DoubleConvertible. Но мне приходит в голову, что с этой реализацией функция всегда будет использовать Double, поэтому эффективность меньше для более мелких типов с плавающей запятой. Или что-то я не вижу? – nbloqs

+1

Метод Swift 3, который расширяет протокол FloatingPoint, использует правильный тип –

ответ

0

Я пришел к выводу, что предварительные вычисления этих констант не могут быть выполнены для общих функций в Swift 3. Вот путь, которым я придерживался: я начал с того, что попытался из @Leo Dabus из предыдущих комментариев (увидеть этот вопрос: How can I convert from degrees to radians?), и придумал следующий код, который терпит неудачу во время выполнения:

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 предполагается быть двойным компилятором, а затем вынужденный литой as! не работает во время выполнения. Также обратите внимание, что я пробовал как функцию, так и синтаксис свойства (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) 
     } 
    } 

Примечание: обменивая пусть на вар ничего не меняет, так как он должно быть.

Итак, наконец, я решил получить синтаксис свойств из упомянутой ссылки, но сосредоточен только на 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 } 
    } 
} 

Несколько дополнительных примечаний:

  • конечные методы не поддерживаются расширения (которые могли бы помочь немного).
  • .pi in Swift 3: Нет, это не ошибка, чтобы не использовать его, поскольку в библиотеке, которую я создаю, я использую много вещей, таких как M_PI_2 и другие π-константы с CGFloat.Таким образом, отбрасывая их на CGFloat во время компиляции, а использование их с аналогичным соглашением об именах с тем, которое предоставляется константами M_XX, в этом конкретном случае совершенно нормально. И проблема с .pi заключается в том, что, насколько мне известно, библиотеки Swift не предоставляют эти другие (абсолютно необходимые для меня) значения. Поэтому для согласованности я просто продолжаю использовать это, и он работает быстро и с соответствующими типами. Я просто не покупаю все вещи стиля у Swift-парней.

Наконец, если кто-то все-таки отвечает на это с трюком я не вижу (что позволяет предвычисления константы и до сих пор используют общие функции, так как я спросить), что будет большим, и принимая новое решение так как правильный ответ будет приятным ...

Смежные вопросы