Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-6919] .ulpOfOne is unnecessarily slow #49468

Closed
jepers opened this issue Feb 3, 2018 · 2 comments
Closed

[SR-6919] .ulpOfOne is unnecessarily slow #49468

jepers opened this issue Feb 3, 2018 · 2 comments
Assignees
Labels
compiler The Swift compiler in itself improvement performance

Comments

@jepers
Copy link

jepers commented Feb 3, 2018

Previous ID SR-6919
Radar None
Original Reporter @jepers
Type Improvement
Status Closed
Resolution Done
Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Improvement, Performance
Assignee @xwu
Priority Medium

md5: 86c403574f9bbd5ddc0f4a55ee58a099

Issue Description:

This (.ulpOfOne) is just one particular example (of a static read-only computed property that should clearly be a constant that shouldn't have to be recalculated every time), and I guess there are many other similar cases.

import Security
import QuartzCore

//-----------------------------------------------------------------------------
// The static property .ulpOfOne is unnecessarily slow (as it seems to perform
// actual function calls to Double(1).ulp on every access). The test() func
// below is almost 5 times faster when setting usingMyUlpOfOne to true.
//-----------------------------------------------------------------------------
let usingMyUlpOfOne = false

extension Double {
    static let myUlpOfOne = Double.ulpOfOne
    init(unitRange i: UInt64) {
        let uoo = usingMyUlpOfOne ? Double.myUlpOfOne : Double.ulpOfOne
        self = Double(i >> Double.exponentBitCount) * (uoo / 2.0)
    }
}

func unsafeRandomBitPatternElements<T>(count: Int) -> [T]
    where T: ExpressibleByIntegerLiteral
{
    var a = [T](repeating: 0, count: count)
    let r = SecRandomCopyBytes(nil, count * MemoryLayout<T>.stride, &a)
    precondition(r == errSecSuccess)
    return a
}

func test() {
    for _ in 0 ..< 10 {
        var sum = Double(0)
        let a: [UInt64] = unsafeRandomBitPatternElements(count: 100_000_000)
        let t0 = CACurrentMediaTime()
        for e in a { sum += Double(unitRange: e) }
        let t1 = CACurrentMediaTime()
        print("time:", t1 - t0, "seconds, ( sum:", sum, ")")
    }
}

print("usingMyUlpOfOne == \(usingMyUlpOfOne):")
test()

//-----------------------------------------------------------------------------
// Example output when run on my MBP late 2013, dev snapshot 2018-01-30:
//-----------------------------------------------------------------------------
//
// usingMyUlpOfOne == false:
// time: 0.506870910001453 seconds, ( sum: 49998178.5861357 )
// time: 0.508704126987141 seconds, ( sum: 50000915.5541131 )
// time: 0.48335500099347 seconds, ( sum: 50002202.6118007 )
// time: 0.490296787000261 seconds, ( sum: 49997649.5106063 )
// time: 0.502311271004146 seconds, ( sum: 49998730.1718012 )
// time: 0.487179237010423 seconds, ( sum: 50001758.3728186 )
// time: 0.530522076995112 seconds, ( sum: 49998896.5738505 )
// time: 0.524156589992344 seconds, ( sum: 50002229.7349948 )
// time: 0.515008457994554 seconds, ( sum: 50001545.8855311 )
// time: 0.50125320898951 seconds, ( sum: 49999437.9996973 )
//
//-----------------------------------------------------------------------------
//
// usingMyUlpOfOne == true:
// time: 0.122320255992236 seconds, ( sum: 49998913.4932828 )
// time: 0.113033984991489 seconds, ( sum: 50000117.6091672 )
// time: 0.111723182984861 seconds, ( sum: 50002081.104568 )
// time: 0.111441375978757 seconds, ( sum: 49997609.1379723 )
// time: 0.111161193985026 seconds, ( sum: 49998541.0318086 )
// time: 0.121517223014962 seconds, ( sum: 49994113.287431 )
// time: 0.116348965995712 seconds, ( sum: 50003603.4877935 )
// time: 0.112562206981238 seconds, ( sum: 49998787.9100643 )
// time: 0.116928763978649 seconds, ( sum: 50003593.6316356 )
// time: 0.111437907005893 seconds, ( sum: 49999608.8038312 )
//
//-----------------------------------------------------------------------------

Is this (eg .ulpOfOne being slow) because of a missing but possible optimization or can't the compiler know that the static computed property need only be actually computed once?

Perhaps Swift needs "pure" or something for it to be knowable?

@belkadan
Copy link
Contributor

belkadan commented Feb 5, 2018

We don't want to cache the value because that would mean allocating space for another global variable in the runtime. But we ought to be able to precompute the value, making the getter a simple return.

@xwu
Copy link
Collaborator

xwu commented Feb 10, 2018

Resolved in PR #14502.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler The Swift compiler in itself improvement performance
Projects
None yet
Development

No branches or pull requests

3 participants