Skip to content

Commit f593cd1

Browse files
[6.0] performance improvements for Duration static members (swiftlang#73439)
* Make static [milli/micro/nano]seconds members on Duration inlinable (swiftlang#73429) This means that they can't use _Int128 for their implementation, but efficient implementation of these using only 64b arithmetic is pretty straightforward, so that's OK. This allows them to be specialized and mostly optimized away in release builds. * Avoid using generic static members on Duration across module boundaries (swiftlang#73419) * Avoid using generic static members on Duration across module boundaries Because the clocks are implemented in Concurrency, but Duration is in the Swift module, these don't get specialized. Add a fully-concrete internal init in Concurrency to avoid the problem. * Call self.init(_high:low:) explicitly. * Add availability annotation.
1 parent af88d18 commit f593cd1

File tree

3 files changed

+48
-15
lines changed

3 files changed

+48
-15
lines changed

stdlib/public/Concurrency/ContinuousClock.swift

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,21 @@ public struct ContinuousClock: Sendable {
3434
public init() { }
3535
}
3636

37+
@available(SwiftStdlib 5.7, *)
38+
extension Duration {
39+
internal init(_seconds s: Int64, nanoseconds n: Int64) {
40+
let (secHi, secLo) = s.multipliedFullWidth(by: 1_000_000_000_000_000_000)
41+
// _nanoseconds is in 0 ..< 1_000_000_000, so the conversion to UInt64
42+
// and multiply cannot overflow. If you somehow trap here, it is because
43+
// the underlying clock hook that produced the time value is implemented
44+
// incorrectly on your platform, but because we trap we can't silently
45+
// get bogus data.
46+
let (low, carry) = secLo.addingReportingOverflow(UInt64(n) * 1_000_000_000)
47+
let high = secHi &+ (carry ? 1 : 0)
48+
self.init(_high: high, low: low)
49+
}
50+
}
51+
3752
@available(SwiftStdlib 5.7, *)
3853
extension Clock where Self == ContinuousClock {
3954
/// A clock that measures time that always increments but does not stop
@@ -60,7 +75,7 @@ extension ContinuousClock: Clock {
6075
seconds: &seconds,
6176
nanoseconds: &nanoseconds,
6277
clock: _ClockID.continuous.rawValue)
63-
return .seconds(seconds) + .nanoseconds(nanoseconds)
78+
return Duration(_seconds: seconds, nanoseconds: nanoseconds)
6479
}
6580

6681
/// The current continuous instant.
@@ -71,8 +86,9 @@ extension ContinuousClock: Clock {
7186
seconds: &seconds,
7287
nanoseconds: &nanoseconds,
7388
clock: _ClockID.continuous.rawValue)
74-
return ContinuousClock.Instant(_value:
75-
.seconds(seconds) + .nanoseconds(nanoseconds))
89+
return Instant(
90+
_value: Duration(_seconds: seconds, nanoseconds: nanoseconds)
91+
)
7692
}
7793

7894
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY

stdlib/public/Concurrency/SuspendingClock.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ extension SuspendingClock: Clock {
6161
seconds: &seconds,
6262
nanoseconds: &nanoseconds,
6363
clock: _ClockID.suspending.rawValue)
64-
return SuspendingClock.Instant(_value:
65-
.seconds(seconds) + .nanoseconds(nanoseconds))
64+
return Instant(
65+
_value: Duration(_seconds: seconds, nanoseconds: nanoseconds)
66+
)
6667
}
6768

6869
/// The minimum non-zero resolution between any two calls to `now`.
@@ -74,7 +75,7 @@ extension SuspendingClock: Clock {
7475
seconds: &seconds,
7576
nanoseconds: &nanoseconds,
7677
clock: _ClockID.suspending.rawValue)
77-
return .seconds(seconds) + .nanoseconds(nanoseconds)
78+
return Duration(_seconds: seconds, nanoseconds: nanoseconds)
7879
}
7980

8081
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY

stdlib/public/core/Duration.swift

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -108,9 +108,13 @@ extension Duration {
108108
///
109109
/// - Returns: A `Duration` representing a given number of seconds.
110110
@available(SwiftStdlib 5.7, *)
111+
@inlinable
111112
public static func seconds<T: BinaryInteger>(_ seconds: T) -> Duration {
112-
return Duration(_attoseconds:
113-
_Int128(seconds).multiplied(by: 1_000_000_000_000_000_000 as UInt64))
113+
guard let high = Int64(exactly: seconds >> 64) else { fatalError() }
114+
let low = UInt64(truncatingIfNeeded: seconds)
115+
var lowScaled = low.multipliedFullWidth(by: 1_000_000_000_000_000_000)
116+
var highScaled = high * 1_000_000_000_000_000_000
117+
return Duration(_high: highScaled + Int64(lowScaled.high), low: lowScaled.low)
114118
}
115119

116120
/// Construct a `Duration` given a duration and scale, taking care so that
@@ -148,11 +152,15 @@ extension Duration {
148152
///
149153
/// - Returns: A `Duration` representing a given number of milliseconds.
150154
@available(SwiftStdlib 5.7, *)
155+
@inlinable
151156
public static func milliseconds<T: BinaryInteger>(
152157
_ milliseconds: T
153158
) -> Duration {
154-
return Duration(_attoseconds:
155-
_Int128(milliseconds).multiplied(by: 1_000_000_000_000_000 as UInt64))
159+
guard let high = Int64(exactly: milliseconds >> 64) else { fatalError() }
160+
let low = UInt64(truncatingIfNeeded: milliseconds)
161+
var lowScaled = low.multipliedFullWidth(by: 1_000_000_000_000_000)
162+
var highScaled = high * 1_000_000_000_000_000
163+
return Duration(_high: highScaled + Int64(lowScaled.high), low: lowScaled.low)
156164
}
157165

158166
/// Construct a `Duration` given a number of seconds milliseconds as a
@@ -173,11 +181,15 @@ extension Duration {
173181
///
174182
/// - Returns: A `Duration` representing a given number of microseconds.
175183
@available(SwiftStdlib 5.7, *)
184+
@inlinable
176185
public static func microseconds<T: BinaryInteger>(
177186
_ microseconds: T
178187
) -> Duration {
179-
return Duration(_attoseconds:
180-
_Int128(microseconds).multiplied(by: 1_000_000_000_000 as UInt64))
188+
guard let high = Int64(exactly: microseconds >> 64) else { fatalError() }
189+
let low = UInt64(truncatingIfNeeded: microseconds)
190+
var lowScaled = low.multipliedFullWidth(by: 1_000_000_000_000)
191+
var highScaled = high * 1_000_000_000_000
192+
return Duration(_high: highScaled + Int64(lowScaled.high), low: lowScaled.low)
181193
}
182194

183195
/// Construct a `Duration` given a number of seconds microseconds as a
@@ -198,11 +210,15 @@ extension Duration {
198210
///
199211
/// - Returns: A `Duration` representing a given number of nanoseconds.
200212
@available(SwiftStdlib 5.7, *)
213+
@inlinable
201214
public static func nanoseconds<T: BinaryInteger>(
202215
_ nanoseconds: T
203216
) -> Duration {
204-
return Duration(_attoseconds:
205-
_Int128(nanoseconds).multiplied(by: 1_000_000_000))
217+
guard let high = Int64(exactly: nanoseconds >> 64) else { fatalError() }
218+
let low = UInt64(truncatingIfNeeded: nanoseconds)
219+
var lowScaled = low.multipliedFullWidth(by: 1_000_000_000)
220+
var highScaled = high * 1_000_000_000
221+
return Duration(_high: highScaled + Int64(lowScaled.high), low: lowScaled.low)
206222
}
207223
}
208224

0 commit comments

Comments
 (0)