-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Some performance improvements in the stdlib, mainly for small string literals #16890
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
Changes from all commits
233c9c4
217a317
a2b0e8f
72038b5
50f7ea5
47383e2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -127,25 +127,30 @@ extension _SmallUTF8String { | |
#endif | ||
} | ||
|
||
@inline(__always) | ||
@inlinable | ||
@effects(readonly) | ||
public // @testable | ||
init?(_ codeUnits: UnsafeBufferPointer<UInt8>) { | ||
#if arch(i386) || arch(arm) | ||
return nil // Never form small strings on 32-bit | ||
#else | ||
let count = codeUnits.count | ||
guard count <= _SmallUTF8String.capacity else { return nil } | ||
self.init() | ||
self._withAllUnsafeMutableBytes { rawBufPtr in | ||
let rawDst = rawBufPtr.baseAddress._unsafelyUnwrappedUnchecked | ||
memcpy_( | ||
dst: rawDst.assumingMemoryBound(to: UInt8.self), | ||
src: codeUnits.baseAddress._unsafelyUnwrappedUnchecked, | ||
count: count | ||
) | ||
|
||
let addr = codeUnits.baseAddress._unsafelyUnwrappedUnchecked | ||
var high: UInt | ||
let lowCount: Int | ||
if count > 8 { | ||
lowCount = 8 | ||
high = _bytesToUInt(addr + 8, count &- 8) | ||
} else { | ||
lowCount = count | ||
high = 0 | ||
} | ||
_sanityCheck(self.count == 0, "overwrote count early?") | ||
self.count = count | ||
high |= (UInt(count) &<< (8*15)) | ||
let low = _bytesToUInt(addr, lowCount) | ||
_storage = (low, high) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the code would be more clear if this was in the if-else blocks. We'd also not need to introduce so many in-scope variables. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to reduce the number of calls to _bytesToUInt. Although llvm can fold away the whole loop, this is not yet done on SIL level, so duplicating _bytes_ToUInt in the then- and else-branch generates an additional loop in SIL. |
||
|
||
// FIXME: support transcoding | ||
if !self.isASCII { return nil } | ||
|
@@ -169,6 +174,18 @@ extension _SmallUTF8String { | |
} | ||
} | ||
|
||
@inline(__always) | ||
@inlinable | ||
func _bytesToUInt(_ input: UnsafePointer<UInt8>, _ c: Int) -> UInt { | ||
var r: UInt = 0 | ||
var shift: Int = 0 | ||
for idx in 0..<c { | ||
r = r | (UInt(input[idx]) &<< shift) | ||
shift = shift &+ 8 | ||
} | ||
return r | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this whole function just a (potentially-unaligned) load and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The whole code is folded away (that's the point of the change). I added a test for that: SILOptimizer/string_literals.swift There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (following up), this code is also called for many other dynamic String initializations, so we'd want efficient code even when it can't be constant folded. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Erik and I discussed changing the UBP representation. I couldn't think of any good reason to keep the (start, end] representation for the buffer, as long as the iterator keeps its representation. |
||
|
||
// | ||
// Small string read interface | ||
// | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you instead put this in the setter for
count
, so that all other code paths can benefit?Also, rather than have a naked
15
, you can usecapacity
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, the count setter would have to mask out the old value, which is not needed here.
Also, I followed the count getter+setter pattern which also hard-code the '15'.