2025 AI & Machine LearningGraphics & GamesDeveloper Tools
WWDC25 · 33 min · AI & Machine Learning / Graphics & Games / Developer Tools
Optimize CPU performance with Instruments
Learn how to optimize your app for Apple silicon with two new hardware-assisted tools in Instruments. We’ll start by covering how to profile your app, then dive deeper by showing every single function called with Processor Trace. We’ll also discuss how to use CPU Counters’ modes to analyze your code for CPU bottlenecks.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 10 snippets
Binary search in Collection
public func binarySearch<E, C>(
needle: E,
haystack: C
) -> C.Index where E: Comparable, C: Collection<E> {
var start = haystack.startIndex
var length = haystack.count
while length > 0 {
let half = length / 2
let middle = haystack.index(start, offsetBy: half)
let middleValue = haystack[middle]
if needle < middleValue {
length = half
} else if needle == middleValue {
return middle
} else {
start = haystack.index(after: middle)
length -= half + 1
}
}
return start
} Throughput benchmark
import Testing
import OSLog
let signposter = OSSignposter(
subsystem: "com.example.apple-samplecode.MyBinarySearch",
category: .pointsOfInterest
)
func search(
name: StaticString,
duration: Duration,
_ search: () -> Void
) {
var now = ContinuousClock.now
var outerIterations = 0
let interval = signposter.beginInterval(name)
let start = ContinuousClock.now
repeat {
search()
outerIterations += 1
now = .now
} while (start.duration(to: now) < duration)
let elapsed = start.duration(to: now)
let seconds = Double(elapsed.components.seconds) +
Double(elapsed.components.attoseconds) / 1e18
let throughput = Double(outerIterations) / seconds
signposter.endInterval(name, interval, "\(throughput) ops/s")
print("\(name): \(throughput) ops/s")
}
let arraySize = 8 << 20
let arrayCount = arraySize / MemoryLayout<Int>.size
let searchCount = 10_000
struct MyBinarySearchTests {
let sortedArray: [Int]
let randomElements: [Int]
init() {
let sortedArray: [Int] = (0..<arrayCount).map { _ in
.random(in: 0..<arrayCount)
}.sorted()
self.randomElements = (0..<searchCount).map { _ in
sortedArray.randomElement()!
}
self.sortedArray = sortedArray
}
func searchCollection() throws {
search(name: "Collection", duration: .seconds(1)) {
for element in randomElements {
_ = binarySearch(needle: element, haystack: sortedArray)
}
}
}
} Binary search in Span
public func binarySearch<E: Comparable>(
needle: E,
haystack: Span<E>
) -> Span<E>.Index {
var start = haystack.indices.startIndex
var length = haystack.count
while length > 0 {
let half = length / 2
let middle = haystack.indices.index(start, offsetBy: half)
let middleValue = haystack[middle]
if needle < middleValue {
length = half
} else if needle == middleValue {
return middle
} else {
start = haystack.indices.index(after: middle)
length -= half + 1
}
}
return start
} Throughput benchmark for binary search in Span
extension MyBinarySearchTests {
func searchSpan() throws {
let span = sortedArray.span
search(name: "Span", duration: .seconds(1)) {
for element in randomElements {
_ = binarySearch(needle: element, haystack: span)
}
}
}
func searchSpanForProcessorTrace() throws {
let span = sortedArray.span
signposter.withIntervalSignpost("Span") {
for element in randomElements[0..<10] {
_ = binarySearch(needle: element, haystack: span)
}
}
}
} Binary search in Span<Int>
public func binarySearchInt(
needle: Int,
haystack: Span<Int>
) -> Span<Int>.Index {
var start = haystack.indices.startIndex
var length = haystack.count
while length > 0 {
let half = length / 2
let middle = haystack.indices.index(start, offsetBy: half)
let middleValue = haystack[middle]
if needle < middleValue {
length = half
} else if needle == middleValue {
return middle
} else {
start = haystack.indices.index(after: middle)
length -= half + 1
}
}
return start
} Throughput benchmark for binary search in Span<Int>
extension MyBinarySearchTests {
func searchSpanInt() throws {
let span = sortedArray.span
search(name: "Span<Int>", duration: .seconds(1)) {
for element in randomElements {
_ = binarySearchInt(needle: element, haystack: span)
}
}
}
} Branchless binary search
public func binarySearchBranchless(
needle: Int,
haystack: Span<Int>
) -> Span<Int>.Index {
var start = haystack.indices.startIndex
var length = haystack.count
while length > 0 {
let remainder = length % 2
length /= 2
let middle = start &+ length
let middleValue = haystack[middle]
if needle > middleValue {
start = middle &+ remainder
}
}
return start
} Throughput benchmark for branchless binary search
extension MyBinarySearchTests {
func searchBranchless() throws {
let span = sortedArray.span
search(name: "Branchless", duration: .seconds(1)) {
for element in randomElements {
_ = binarySearchBranchless(needle: element, haystack: span)
}
}
}
} Eytzinger binary search
public func binarySearchEytzinger(
needle: Int,
haystack: Span<Int>
) -> Span<Int>.Index {
var start = haystack.indices.startIndex.advanced(by: 1)
let length = haystack.count
while start < length {
let value = haystack[start]
start *= 2
if value < needle {
start += 1
}
}
return start >> ((~start).trailingZeroBitCount + 1)
} Throughput benchmark for Eytzinger binary search
struct MyBinarySearchEytzingerTests {
let eytzingerArray: [Int]
let randomElements: [Int]
static func reorderEytzinger(_ input: [Int], array: inout [Int], sourceIndex: Int, resultIndex: Int) -> Int {
var sourceIndex = sourceIndex
if resultIndex < array.count {
sourceIndex = reorderEytzinger(input, array: &array, sourceIndex: sourceIndex, resultIndex: 2 * resultIndex)
array[resultIndex] = input[sourceIndex]
sourceIndex = reorderEytzinger(input, array: &array, sourceIndex: sourceIndex + 1, resultIndex: 2 * resultIndex + 1)
}
return sourceIndex
}
init() {
let sortedArray: [Int] = (0..<arrayCount).map { _ in
.random(in: 0..<arrayCount)
}.sorted()
var eytzingerArray: [Int] = Array(repeating: 0, count: arrayCount + 1)
_ = Self.reorderEytzinger(sortedArray, array: &eytzingerArray, sourceIndex: 0, resultIndex: 1)
self.randomElements = (0..<searchCount).map { _ in
sortedArray.randomElement()!
}
self.eytzingerArray = eytzingerArray
}
func searchEytzinger() throws {
let span = eytzingerArray.span
search(name: "Eytzinger", duration: .seconds(1)) {
for element in randomElements {
_ = binarySearchEytzinger(needle: element, haystack: span)
}
}
}
} Resources
Related sessions
-
25 min -
43 min -
35 min -
36 min -
20 min -
32 min