Avoid curried thunks (workaround SR-12115) (#1374)

Motivation:

Calling `function(otherFunction)` allocates, but
`function({ otherFunction($0) }` does not.

See: SR-12115 and SR-12116.

Modifications:

Avoid passing functions directly; pass them in new closures instead.

Result:

Fewer allocations.
This commit is contained in:
George Barnett 2020-01-31 17:57:30 +00:00 committed by GitHub
parent 2bf296958b
commit b4483e5c49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 37 additions and 24 deletions

View File

@ -398,13 +398,13 @@ class BaseSocket: Selectable, BaseSocketProtocol {
switch address {
case .v4(let address):
var addr = address.address
try addr.withSockAddr(doBind)
try addr.withSockAddr({ try doBind(ptr: $0, bytes: $1) })
case .v6(let address):
var addr = address.address
try addr.withSockAddr(doBind)
try addr.withSockAddr({ try doBind(ptr: $0, bytes: $1) })
case .unixDomainSocket(let address):
var addr = address.address
try addr.withSockAddr(doBind)
try addr.withSockAddr({ try doBind(ptr: $0, bytes: $1) })
}
}
}

View File

@ -96,7 +96,7 @@ class BaseStreamSocketChannel<Socket: SocketProtocol>: BaseSocketChannel<Socket>
// Reset reader and writerIndex and so allow to have the buffer filled again. This is better here than at
// the end of the loop to not do an allocation when the loop exits.
buffer.clear()
switch try buffer.withMutableWritePointer(body: self.socket.read(pointer:)) {
switch try buffer.withMutableWritePointer(body: { try self.socket.read(pointer: $0) }) {
case .processed(let bytesRead):
if bytesRead > 0 {
let mayGrow = recvAllocator.record(actualReadBytes: bytesRead)

View File

@ -258,7 +258,7 @@ extension ByteBuffer {
@discardableResult
@inlinable
public mutating func readWithUnsafeReadableBytes(_ body: (UnsafeRawBufferPointer) throws -> Int) rethrows -> Int {
let bytesRead = try self.withUnsafeReadableBytes(body)
let bytesRead = try self.withUnsafeReadableBytes({ try body($0) })
self._moveReaderIndex(forwardBy: bytesRead)
return bytesRead
}
@ -273,7 +273,7 @@ extension ByteBuffer {
/// - returns: The value `body` returned in the second tuple component.
@inlinable
public mutating func readWithUnsafeReadableBytes<T>(_ body: (UnsafeRawBufferPointer) throws -> (Int, T)) rethrows -> T {
let (bytesRead, ret) = try self.withUnsafeReadableBytes(body)
let (bytesRead, ret) = try self.withUnsafeReadableBytes({ try body($0) })
self._moveReaderIndex(forwardBy: bytesRead)
return ret
}
@ -289,7 +289,7 @@ extension ByteBuffer {
@discardableResult
@inlinable
public mutating func readWithUnsafeMutableReadableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> Int) rethrows -> Int {
let bytesRead = try self.withUnsafeMutableReadableBytes(body)
let bytesRead = try self.withUnsafeMutableReadableBytes({ try body($0) })
self._moveReaderIndex(forwardBy: bytesRead)
return bytesRead
}
@ -304,7 +304,7 @@ extension ByteBuffer {
/// - returns: The value `body` returned in the second tuple component.
@inlinable
public mutating func readWithUnsafeMutableReadableBytes<T>(_ body: (UnsafeMutableRawBufferPointer) throws -> (Int, T)) rethrows -> T {
let (bytesRead, ret) = try self.withUnsafeMutableReadableBytes(body)
let (bytesRead, ret) = try self.withUnsafeMutableReadableBytes({ try body($0) })
self._moveReaderIndex(forwardBy: bytesRead)
return ret
}

View File

@ -515,7 +515,7 @@ public struct ByteBuffer {
if minimumWritableBytes > 0 {
self.reserveCapacity(self.writerIndex + minimumWritableBytes)
}
let bytesWritten = try self.withUnsafeMutableWritableBytes(body)
let bytesWritten = try self.withUnsafeMutableWritableBytes({ try body($0) })
self._moveWriterIndex(to: self._writerIndex + _toIndex(bytesWritten))
return bytesWritten
}
@ -524,7 +524,7 @@ public struct ByteBuffer {
@discardableResult
@inlinable
public mutating func writeWithUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> Int) rethrows -> Int {
return try self.writeWithUnsafeMutableBytes(minimumWritableBytes: 0, body)
return try self.writeWithUnsafeMutableBytes(minimumWritableBytes: 0, { try body($0) })
}
/// This vends a pointer to the storage of the `ByteBuffer`. It's marked as _very unsafe_ because it might contain

View File

@ -433,10 +433,10 @@ final class PendingDatagramWritesManager: PendingWritesManager {
return try self.triggerWriteOperations { writeMechanism in
switch writeMechanism {
case .scalarBufferWrite:
return try triggerScalarBufferWrite(scalarWriteOperation: scalarWriteOperation)
return try triggerScalarBufferWrite(scalarWriteOperation: { try scalarWriteOperation($0, $1, $2) })
case .vectorBufferWrite:
do {
return try triggerVectorBufferWrite(vectorWriteOperation: vectorWriteOperation)
return try triggerVectorBufferWrite(vectorWriteOperation: { try vectorWriteOperation($0) })
} catch {
// If the error we just hit is recoverable, we fall back to single write mode to
// isolate exactly which write triggered the problem.
@ -444,7 +444,7 @@ final class PendingDatagramWritesManager: PendingWritesManager {
throw error
}
return try triggerScalarBufferWrite(scalarWriteOperation: scalarWriteOperation)
return try triggerScalarBufferWrite(scalarWriteOperation: { try scalarWriteOperation($0, $1, $2) })
}
case .scalarFileWrite:
preconditionFailure("PendingDatagramWritesManager was handed a file write")
@ -524,7 +524,7 @@ final class PendingDatagramWritesManager: PendingWritesManager {
msgs: self.msgs,
addresses: self.addresses,
storageRefs: self.storageRefs,
vectorWriteOperation),
{ try vectorWriteOperation($0) }),
messages: self.msgs)
}

View File

@ -339,11 +339,11 @@ final class PendingStreamWritesManager: PendingWritesManager {
return try self.triggerWriteOperations { writeMechanism in
switch writeMechanism {
case .scalarBufferWrite:
return try triggerScalarBufferWrite(scalarBufferWriteOperation)
return try triggerScalarBufferWrite({ try scalarBufferWriteOperation($0) })
case .vectorBufferWrite:
return try triggerVectorBufferWrite(vectorBufferWriteOperation)
return try triggerVectorBufferWrite({ try vectorBufferWriteOperation($0) })
case .scalarFileWrite:
return try triggerScalarFileWrite(scalarFileWriteOperation)
return try triggerScalarFileWrite({ try scalarFileWriteOperation($0, $1, $2) })
case .nothingToBeWritten:
assertionFailure("called \(#function) with nothing available to be written")
return .writtenCompletely
@ -378,7 +378,7 @@ final class PendingStreamWritesManager: PendingWritesManager {
switch self.state[0].data {
case .byteBuffer(let buffer):
return self.didWrite(itemCount: 1, result: try buffer.withUnsafeReadableBytes(operation))
return self.didWrite(itemCount: 1, result: try buffer.withUnsafeReadableBytes({ try operation($0) }))
case .fileRegion:
preconditionFailure("called \(#function) but first item to write was a FileRegion")
}
@ -414,7 +414,7 @@ final class PendingStreamWritesManager: PendingWritesManager {
let result = try doPendingWriteVectorOperation(pending: self.state,
iovecs: self.iovecs,
storageRefs: self.storageRefs,
operation)
{ try operation($0) })
return self.didWrite(itemCount: result.itemCount, result: result.writeResult)
}

View File

@ -185,13 +185,13 @@ public enum SocketAddress: CustomStringConvertible {
switch self {
case .v4(let addr):
var address = addr.address
return try address.withSockAddr(body)
return try address.withSockAddr({ try body($0, $1) })
case .v6(let addr):
var address = addr.address
return try address.withSockAddr(body)
return try address.withSockAddr({ try body($0, $1) })
case .unixDomainSocket(let addr):
var address = addr.address
return try address.withSockAddr(body)
return try address.withSockAddr({ try body($0, $1) })
}
}

View File

@ -18,8 +18,8 @@ services:
test:
image: swift-nio:18.04-5.0
environment:
- MAX_ALLOCS_ALLOWED_1000_reqs_1_conn=30990
- MAX_ALLOCS_ALLOWED_1_reqs_1000_conn=962050
- MAX_ALLOCS_ALLOWED_1000_reqs_1_conn=31990
- MAX_ALLOCS_ALLOWED_1_reqs_1000_conn=965050
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4500
- MAX_ALLOCS_ALLOWED_bytebuffer_lots_of_rw=2100
- MAX_ALLOCS_ALLOWED_future_lots_of_callbacks=75010

13
docs/workarounds.md Normal file
View File

@ -0,0 +1,13 @@
# Workarounds
The following list of PRs and commits were applied in order to work around bugs
or cases where the Swift compiler was unable to sufficiently optimize the code:
- https://github.com/apple/swift-nio/pull/1374
- https://github.com/apple/swift-nio-ssl/pull/176
- https://github.com/apple/swift-nio/pull/1325
- https://github.com/apple/swift-nio/pull/1299
- https://github.com/apple/swift-nio/pull/1252
- https://github.com/apple/swift-nio/pull/494
- https://github.com/apple/swift-nio/pull/420
- https://github.com/apple/swift-nio/commit/abc963cfe1e1d4856c41421c9d53ea778102e9e8