[5.0] String performance and code size improvements (#21081)
* [String] In-register smol ASCII string compare
Compare small strings in-register when they store ASCII (and thus NFC)
contents.
* [String] String-from-whole-Substring fast-path.
Add in a fast-path for Strings created from Substring which covers the
entire String. Put String-from-Substring behind a non-inlinable
resilience barrier for future flexibility.
diff --git a/stdlib/public/core/StringComparison.swift b/stdlib/public/core/StringComparison.swift
index 684bf90..9a93af5 100644
--- a/stdlib/public/core/StringComparison.swift
+++ b/stdlib/public/core/StringComparison.swift
@@ -18,9 +18,31 @@
_ lhs: _StringGuts, _ rhs: _StringGuts, expecting: _StringComparisonResult
) -> Bool {
if lhs.rawBits == rhs.rawBits { return expecting == .equal }
+ return _stringCompareWithSmolCheck(lhs, rhs, expecting: expecting)
+}
+
+@usableFromInline
+@_effects(readonly)
+internal func _stringCompareWithSmolCheck(
+ _ lhs: _StringGuts, _ rhs: _StringGuts, expecting: _StringComparisonResult
+) -> Bool {
+ // ASCII small-string fast-path:
+ if lhs.isSmallASCII && rhs.isSmallASCII {
+ let lhsRaw = lhs.asSmall._storage
+ let rhsRaw = rhs.asSmall._storage
+
+ if lhsRaw.0 != rhsRaw.0 {
+ return _lexicographicalCompare(
+ lhsRaw.0.byteSwapped, rhsRaw.0.byteSwapped, expecting: expecting)
+ }
+ return _lexicographicalCompare(
+ lhsRaw.1.byteSwapped, rhsRaw.1.byteSwapped, expecting: expecting)
+ }
+
return _stringCompareInternal(lhs, rhs, expecting: expecting)
}
+@inline(never) // Keep `_stringCompareWithSmolCheck` fast-path fast
@usableFromInline
@_effects(readonly)
internal func _stringCompareInternal(
diff --git a/stdlib/public/core/StringCreate.swift b/stdlib/public/core/StringCreate.swift
index 49fff68..ec032c1 100644
--- a/stdlib/public/core/StringCreate.swift
+++ b/stdlib/public/core/StringCreate.swift
@@ -169,5 +169,16 @@
) -> String {
return String._fromCodeUnits(utf16, encoding: UTF16.self, repair: true)!.0
}
+
+ @usableFromInline
+ internal static func _fromSubstring(
+ _ substring: __shared Substring
+ ) -> String {
+ if substring._offsetRange == substring._wholeString._offsetRange {
+ return substring._wholeString
+ }
+
+ return substring._withUTF8 { return String._uncheckedFromUTF8($0) }
+ }
}
diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift
index f6413e2..718d132 100644
--- a/stdlib/public/core/StringGuts.swift
+++ b/stdlib/public/core/StringGuts.swift
@@ -90,6 +90,10 @@
@inline(__always) get { return _object.isSmall }
}
+ internal var isSmallASCII: Bool {
+ @inline(__always) get { return _object.isSmall && _object.smallIsASCII }
+ }
+
@inlinable
internal var asSmall: _SmallString {
@inline(__always) get { return _SmallString(_object) }
diff --git a/stdlib/public/core/Substring.swift b/stdlib/public/core/Substring.swift
index 6ffcc0a..33e3615 100644
--- a/stdlib/public/core/Substring.swift
+++ b/stdlib/public/core/Substring.swift
@@ -20,7 +20,7 @@
/// - Complexity: O(*n*), where *n* is the length of `substring`.
@inlinable
public init(_ substring: __shared Substring) {
- self = substring._withUTF8 { return String._uncheckedFromUTF8($0) }
+ self = String._fromSubstring(substring)
}
}