-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[SR-14516] Swift generates subscript.read accessors that allocate! #56868
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
Comments
CC @eeckstein & @rjmccall because this seems to be coro related. |
@swift-ci create |
2024 edition:
still calls malloc on today's Swift 6 preview. |
CC: @nate-chandler |
This particular repro might be fixed in 6.2. If I re-run my original program, I do not see any traces of allocations functions. The following DTrace/
8000
AWK program prints any function that was called more than 1000 times. No allocations in there (would be
|
Yes, repro is no longer valid. Expanded (and cleaned it up a little): extension CircularBuffer {
@inline(never)
func HARNESS() {
let x = first! // << this allocates as it calls generic specialization <repro.NIOAny> of repro.CircularBuffer.subscript.read : (repro.CircularBuffer<A>.Index) -> A
precondition(x.isGood())
}
}
protocol MyProtocol {
init()
func isGood() -> Bool
}
struct CircularBuffer<d: MyProtocol>{
var e: ContiguousArray<d?> = [.init()]
struct Index: Comparable {
var f: Int = 0
var i: Int {
return f
}
static func == (a: Index, j: Index) -> Bool {
return a.i > j.i
}
static func < (aa: Index, j: Index) -> Bool {
return aa.i < j.i
}
}
func index(after: Index) -> Index {
return index(after, offsetBy: 1)
}
subscript(k: Index) -> d {
get {
return e[k.i]!
}
}
var startIndex = Index()
var endIndex = Index()
}
extension CircularBuffer: Collection {}
enum XY {
case X
case Y
}
struct NIOAny: MyProtocol {
let av: aw = .ay(())
enum aw {
case ax(XY)
case ay(Any)
init<az>(a: az) {
self = .ay(a)
}
}
@inline(never)
func isGood() -> Bool {
switch self.av {
case .ay(let value):
if value as? () != nil {
return true
} else {
return false
}
default:
return false
}
}
}
let b = CircularBuffer<NIOAny>()
for _ in 0..<10000 {
b.HARNESS()
} compile & run
|
However it is still emitting the allocating read subscript accessor:
|
Attachment: Download
Environment
Apple Swift version 5.4 (swiftlang-1205.0.22.2 clang-1205.0.19.29)
Additional Detail from JIRA
md5: 06a0e70a5391143833033eeeb67e35f7
Issue Description:
The following (sorry, this used to be 1700 lines, and
creduce
reduced this) program allocates 10,000 times:here's how it goes:
b.F
callsb.first
(which is provided automatically from Collection)b.first
calls the subscript's read accessor which has an unconditionalmalloc
(for coroutines I think) in there 🙁the read accessors really shouldn't allocate as that defeats many other optimisations.
Full program attached.
Repro:
See how we get 10,000 allocations (malloc) through the subscript.read by calling F 10,000 times?
This seems to be a Swift 5.4 regression. We found this issue in the NIO CI which regresses allocations in a few allocation counter tests but only in 5.4.
Also affects
In case you're into
creduce
, this is the interestingness test I used (for Linux)The text was updated successfully, but these errors were encountered: