Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

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 ↗

Transcript all transcripts

Chapters

Code shown on screen · 10 snippets

Binary search in Collection swift · at 6:37 ↗
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 swift · at 7:49 ↗
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
    }

    @Test func searchCollection() throws {
        search(name: "Collection", duration: .seconds(1)) {
            for element in randomElements {
                _ = binarySearch(needle: element, haystack: sortedArray)
            }
        }
    }
}
Binary search in Span swift · at 13:46 ↗
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 swift · at 15:09 ↗
extension MyBinarySearchTests {
    @Test func searchSpan() throws {
        let span = sortedArray.span
        search(name: "Span", duration: .seconds(1)) {
            for element in randomElements {
                _ = binarySearch(needle: element, haystack: span)
            }
        }
    }

    @Test 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> swift · at 19:17 ↗
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> swift · at 23:04 ↗
extension MyBinarySearchTests {
    @Test 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 swift · at 26:34 ↗
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 swift · at 27:20 ↗
extension MyBinarySearchTests {
    @Test 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 swift · at 29:27 ↗
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 swift · at 30:34 ↗
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
    }

    @Test func searchEytzinger() throws {
        let span = eytzingerArray.span
        search(name: "Eytzinger", duration: .seconds(1)) {
            for element in randomElements {
                _ = binarySearchEytzinger(needle: element, haystack: span)
            }
        }
    }
}

Resources