Skip to content

Commit 53ccd9e

Browse files
committed
[string] Less inlining for code size.
Less inlining for hashing and comparison. Saves code size on very frequent String comparison in exchange for costing us in some of our ridiculuous micro-benchmarks. Also adds in more _effects for better codegen
1 parent 948655e commit 53ccd9e

File tree

4 files changed

+47
-17
lines changed

4 files changed

+47
-17
lines changed

stdlib/public/core/StringComparable.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,15 @@ extension String : Equatable {
6868
@inlinable @inline(__always) // For the bitwise comparision
6969
@_effects(readonly)
7070
public static func == (lhs: String, rhs: String) -> Bool {
71-
return _stringCompare(lhs._guts, nil, rhs._guts, nil, expecting: .equal)
71+
return _stringCompare(lhs._guts, rhs._guts, expecting: .equal)
7272
}
7373
}
7474

7575
extension String : Comparable {
7676
@inlinable @inline(__always) // For the bitwise comparision
7777
@_effects(readonly)
7878
public static func < (lhs: String, rhs: String) -> Bool {
79-
return _stringCompare(lhs._guts, nil, rhs._guts, nil, expecting: .less)
79+
return _stringCompare(lhs._guts, rhs._guts, expecting: .less)
8080
}
8181
}
8282

stdlib/public/core/StringComparison.swift

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,42 @@
1313
import SwiftShims
1414

1515

16-
@inlinable @inline(__always) // Fold away opt range and top-level fast-paths
16+
@usableFromInline
1717
@_effects(readonly)
1818
internal func _stringCompare(
19-
_ lhs: _StringGuts, _ lhsRange: Range<Int>?,
20-
_ rhs: _StringGuts, _ rhsRange: Range<Int>?,
19+
_ lhs: _StringGuts, _ rhs: _StringGuts,
20+
expecting: _StringComparisonResult
21+
) -> Bool {
22+
_sanityCheck(expecting == .equal || expecting == .less)
23+
if lhs.rawBits == rhs.rawBits {
24+
return expecting == .equal
25+
}
26+
if _fastPath(lhs.isFastUTF8 && rhs.isFastUTF8) {
27+
let isNFC = lhs.isNFC && rhs.isNFC
28+
return lhs.withFastUTF8 { utf8Left in
29+
return rhs.withFastUTF8 { utf8Right in
30+
if isNFC {
31+
let cmp = _binaryCompare(utf8Left, utf8Right)
32+
if expecting == .equal {
33+
return cmp == 0
34+
}
35+
_sanityCheck(expecting == .less)
36+
return cmp < 0
37+
}
38+
39+
return _stringCompare(utf8Left, utf8Right, expecting: expecting)
40+
}
41+
}
42+
}
43+
return _stringCompareSlow(
44+
lhs, 0..<lhs.count, rhs, 0..<rhs.count, expecting: expecting)
45+
}
46+
47+
@usableFromInline
48+
@_effects(readonly)
49+
internal func _stringCompare(
50+
_ lhs: _StringGuts, _ lhsRange: Range<Int>,
51+
_ rhs: _StringGuts, _ rhsRange: Range<Int>,
2152
expecting: _StringComparisonResult
2253
) -> Bool {
2354
_sanityCheck(expecting == .equal || expecting == .less)

stdlib/public/core/StringHashable.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ extension String : Hashable {
1818
///
1919
/// - Parameter hasher: The hasher to use when combining the components
2020
/// of this instance.
21-
@inlinable // For pre-normal fast paths
2221
public func hash(into hasher: inout Hasher) {
2322
if _fastPath(self._guts.isNFCFastUTF8) {
2423
self._guts.withFastUTF8 {
@@ -38,7 +37,8 @@ extension StringProtocol {
3837
///
3938
/// - Parameter hasher: The hasher to use when combining the components
4039
/// of this instance.
41-
@inlinable
40+
@_specialize(where Self == String)
41+
@_specialize(where Self == Substring)
4242
public func hash(into hasher: inout Hasher) {
4343
_gutsSlice._normalizedHash(into: &hasher)
4444
}

stdlib/public/core/StringObject.swift

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,9 +1020,9 @@ extension _StringObject {
10201020
return sharedStorage.start
10211021
}
10221022

1023-
@inlinable
1023+
@usableFromInline
10241024
internal var sharedUTF8: UnsafeBufferPointer<UInt8> {
1025-
@inline(__always) get {
1025+
@_effects(releasenone) @inline(never) get {
10261026
_sanityCheck(largeFastIsShared)
10271027
let start = self.getSharedUTF8Start()
10281028
return UnsafeBufferPointer(start: start, count: largeCount)
@@ -1121,22 +1121,21 @@ extension _StringObject {
11211121
internal var fastUTF8: UnsafeBufferPointer<UInt8> {
11221122
@inline(__always) get {
11231123
_sanityCheck(self.isLarge && self.providesFastUTF8)
1124-
let start: UnsafePointer<UInt8>
1125-
let count = self.largeCount
11261124
if _slowPath(self.largeFastIsShared) {
1127-
start = getSharedUTF8Start()
1128-
} else {
1129-
start = self.nativeUTF8Start
1125+
return sharedUTF8
11301126
}
1131-
return UnsafeBufferPointer(start: start, count: count)
1127+
return UnsafeBufferPointer(
1128+
start: self.nativeUTF8Start, count: self.largeCount)
11321129
}
11331130
}
11341131

11351132
// Whether the object stored can be bridged directly as a NSString
11361133
@usableFromInline // @opaque
11371134
internal var hasObjCBridgeableObject: Bool {
1138-
// Currently, all mortal objects can zero-cost bridge
1139-
return !self.isImmortal
1135+
@_effects(releasenone) get {
1136+
// Currently, all mortal objects can zero-cost bridge
1137+
return !self.isImmortal
1138+
}
11401139
}
11411140

11421141
// Fetch the stored subclass of NSString for bridging

0 commit comments

Comments
 (0)