Skip to content

Improve BumpPtrAllocator performance #608

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

Merged
merged 1 commit into from
Aug 19, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 37 additions & 22 deletions Sources/SwiftSyntax/BumpPtrAllocator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,27 @@ public class BumpPtrAllocator {
static private var SLAB_ALIGNMENT: Int = 8

private var slabs: [Slab]
/// Points to the next unused address in `slabs.last`.
private var currentPtr: UnsafeMutableRawPointer?
/// Points to the end address of `slabs.last`. If `slabs` is not empty, this
/// is equivalent to `slabs.last!.baseAddress! + slabs.last!.count`
private var endPtr: UnsafeMutableRawPointer?
/// Pair of pointers in the current slab.
/// - pointer: Points to the next unused address in `slabs.last`.
/// - end: Points to the end address of `slabs.last`. This is equivalent to
/// `slabs.last!.baseAddress! + slabs.last!.count`
/// 'nil' if `slabs` is empty.
private var current: (pointer: UnsafeMutableRawPointer,
end: UnsafeMutableRawPointer)?
private var customSizeSlabs: [Slab]
private var _totalBytesAllocated: Int

public init() {
slabs = []
currentPtr = nil
endPtr = nil
current = nil
customSizeSlabs = []
_totalBytesAllocated = 0
}

deinit {
/// Deallocate all memory.
_totalBytesAllocated = 0
currentPtr = nil
endPtr = nil
current = nil
while let slab = slabs.popLast() {
slab.deallocate()
}
Expand All @@ -59,9 +59,28 @@ public class BumpPtrAllocator {
let newSlabSize = Self.slabSize(at: slabs.count)
let newSlab = Slab.allocate(
byteCount: newSlabSize, alignment: Self.SLAB_ALIGNMENT)
let pointer = newSlab.baseAddress!
current = (pointer, pointer.advanced(by: newSlabSize))
slabs.append(newSlab)
currentPtr = newSlab.baseAddress!
endPtr = currentPtr!.advanced(by: newSlabSize)
}

/// Allocate 'byteCount' of memory from the current slab if available.
private func allocateFromCurrentSlab(
_ byteCount: Int,
_ alignment: Int
) -> UnsafeMutableRawBufferPointer? {
guard let current = self.current else {
return nil
}

let aligned = current.pointer.alignedUp(toMultipleOf: alignment)
guard byteCount <= aligned.distance(to: current.end) else {
return nil
}

// Bump the pointer, and return the allocated buffer.
self.current = (aligned + byteCount, current.end)
return .init(start: aligned, count: byteCount)
}

/// Allocate 'byteCount' of memory.
Expand All @@ -70,7 +89,7 @@ public class BumpPtrAllocator {
/// Clients should never call `deallocate()` on the returned buffer.
public func allocate(byteCount: Int, alignment: Int) -> UnsafeMutableRawBufferPointer {

precondition(alignment <= Self.SLAB_ALIGNMENT)
assert(alignment <= Self.SLAB_ALIGNMENT)
guard byteCount > 0 else {
return .init(start: nil, count: 0)
}
Expand All @@ -79,12 +98,8 @@ public class BumpPtrAllocator {
_totalBytesAllocated += byteCount

// Check if the current slab has enough space.
if !slabs.isEmpty {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the "unnecessary slab array access."

let aligned = currentPtr!.alignedUp(toMultipleOf: alignment)
if aligned + byteCount <= endPtr! {
currentPtr = aligned + byteCount
return .init(start: aligned, count: byteCount)
}
if let allocated = allocateFromCurrentSlab(byteCount, alignment) {
return allocated
}

// If the size is too big, allocate a dedicated slab for it.
Expand All @@ -97,10 +112,10 @@ public class BumpPtrAllocator {

// Otherwise, start a new slab and try again.
startNewSlab()
let aligned = currentPtr!.alignedUp(toMultipleOf: alignment)
assert(aligned + byteCount <= endPtr!, "Unable to allocate memory!")
currentPtr = aligned + byteCount
return .init(start: aligned, count: byteCount)
if let allocated = allocateFromCurrentSlab(byteCount, alignment) {
return allocated
}
fatalError("Unable to allocate memory!")
}

/// Allocate a chunk of bound memory of `MemoryLayout<T>.stride * count'.
Expand Down