Skip to content

Commit c0c530a

Browse files
committed
[String] Speed up constant factors on comparison.
Include some tuning and tweaking to reduce the constant factors involved in string comparison. This yields considerable improvement on our micro-benchmarks, and allows us to make less inlinable code and have a smaller ABI surface area. Adds more extensive testing of corner cases in our existing fast-paths.
1 parent 1706d4c commit c0c530a

File tree

6 files changed

+259
-214
lines changed

6 files changed

+259
-214
lines changed

stdlib/public/core/NormalizedCodeUnitIterator.swift

Lines changed: 13 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -141,34 +141,6 @@ internal struct _NormalizedUTF8CodeUnitIterator: IteratorProtocol {
141141

142142
return utf8Buffer[bufferIndex]
143143
}
144-
145-
internal mutating func compare(
146-
with other: _NormalizedUTF8CodeUnitIterator
147-
) -> _StringComparisonResult {
148-
var mutableOther = other
149-
150-
for cu in self {
151-
if let otherCU = mutableOther.next() {
152-
let result = _lexicographicalCompare(cu, otherCU)
153-
if result == .equal {
154-
continue
155-
} else {
156-
return result
157-
}
158-
} else {
159-
//other returned nil, we are greater
160-
return .greater
161-
}
162-
}
163-
164-
//we ran out of code units, either we are equal, or only we ran out and
165-
//other is greater
166-
if let _ = mutableOther.next() {
167-
return .less
168-
} else {
169-
return .equal
170-
}
171-
}
172144
}
173145

174146
extension _NormalizedUTF8CodeUnitIterator: Sequence { }
@@ -463,16 +435,6 @@ extension _NormalizedUTF8CodeUnitIterator_2 {
463435
@inline(__always)
464436
@_effects(releasenone)
465437
private mutating func fastPathFill() -> (numRead: Int, numWritten: Int) {
466-
// Quick check if a scalar is NFC and a segment starter
467-
@inline(__always) func isNFCStarter(_ scalar: Unicode.Scalar) -> Bool {
468-
// Fast-path: All scalars up through U+02FF are NFC and have boundaries
469-
// before them
470-
if scalar.value < 0x300 { return true }
471-
472-
// Otherwise, consult the properties
473-
return scalar._hasNormalizationBoundaryBefore && scalar._isNFCQCYes
474-
}
475-
476438
// TODO: Additional fast-path: All CCC-ascending NFC_QC segments are NFC
477439
// TODO: Just freakin do normalization and don't bother with ICU
478440
var outputCount = 0
@@ -490,7 +452,7 @@ extension _NormalizedUTF8CodeUnitIterator_2 {
490452

491453
if _slowPath(
492454
!utf8.hasNormalizationBoundary(before: inputCount &+ len)
493-
|| !isNFCStarter(scalar)
455+
|| !scalar._isNFCStarter
494456
) {
495457
break
496458
}
@@ -516,7 +478,7 @@ extension _NormalizedUTF8CodeUnitIterator_2 {
516478
if _slowPath(
517479
!gutsSlice.foreignHasNormalizationBoundary(
518480
before: startIdx.encoded(offsetBy: len))
519-
|| !isNFCStarter(scalar)
481+
|| !scalar._isNFCStarter
520482
) {
521483
break
522484
}
@@ -577,29 +539,22 @@ extension _NormalizedUTF8CodeUnitIterator_2 {
577539

578540
@_effects(readonly)
579541
internal mutating func compare(
580-
with other: _NormalizedUTF8CodeUnitIterator_2
581-
) -> _StringComparisonResult {
582-
var iter = self
542+
with other: _NormalizedUTF8CodeUnitIterator_2,
543+
expecting: _StringComparisonResult
544+
) -> Bool {
583545
var mutableOther = other
584546

585-
while let cu = iter.next() {
586-
if let otherCU = mutableOther.next() {
587-
let result = _lexicographicalCompare(cu, otherCU)
588-
if result == .equal {
589-
continue
590-
}
591-
return result
547+
for cu in self {
548+
guard let otherCU = mutableOther.next() else {
549+
// We have more code units, therefore we are greater
550+
return false
592551
}
593-
//other returned nil, we are greater
594-
return .greater
552+
if cu == otherCU { continue }
553+
return expecting == .less ? cu < otherCU : false
595554
}
596555

597-
//we ran out of code units, either we are equal, or only we ran out and
598-
//other is greater
599-
if let _ = mutableOther.next() {
600-
return .less
601-
}
602-
return .equal
556+
// We have exhausted our code units. We are less if there's more remaining
557+
return mutableOther.next() == nil ? expecting == .equal : expecting == .less
603558
}
604559
}
605560

0 commit comments

Comments
 (0)