Avatar ·

Производительность сортировки массивов в Swift Beta: почему так медленно и как ускорить?

Всем привет! Я только начал изучать Swift и столкнулся с очень странной проблемой при сортировке массивов. Использую Xcode 6 Beta, и заметил, что сортировка работает очень медленно по сравнению с Objective-C. Вот простой код: ```swift var numbers = [Int]() for i in 0..<10000 { numbers.append(Int(arc4random_uniform(1000))) } let start = CFAbsoluteTimeGetCurrent() let sorted = numbers.sorted { $0 < $1 } let end = CFAbsoluteTimeGetCurrent() print("Время сортировки: \(end - start) секунд") ``` На реальных данных (массив из 10 000 элементов) сортировка занимает около 0.5-1 секунды, что в 10 раз медленнее, чем аналогичный код на Objective-C. При этом компилятор вроде бы оптимизирует код (включена оптимизация -O). Я попробовал разные варианты: sorted(), sort(), даже писал свою сортировку — результат тот же. Может, я что-то делаю не так? Или это баг бета-версии? Есть ли способы ускорить сортировку? Заранее спасибо за помощь! <<>>

Причина медленной работы

Проблема действительно характерна для ранних бета-версий Swift (Xcode 6). Основная причина — неоптимизированная работа с замыканиями и отсутствие инлайнинга при уровне оптимизации по умолчанию. В Swift Beta компилятор часто не мог эффективно оптимизировать замыкания, переданные в функции высшего порядка, что приводило к многократному созданию и вызову оберток.

Решение: используйте оператор < напрямую

Самый простой способ ускорить сортировку — передать оператор сравнения напрямую, а не замыкание:

let sorted = numbers.sorted(<) // Используем оператор < как функцию

Это работает, потому что операторы в Swift — это обычные функции, и компилятор может их инлайнить. Время сортировки сокращается до 0.05-0.1 секунды — в 10 раз быстрее!

Дополнительные оптимизации

  • Включите полную оптимизацию: В настройках проекта (Build Settings) установите Optimization Level в -Owholemodule для Swift. Это включает межмодульную оптимизацию.
  • Используйте sort() вместо sorted() если нужно отсортировать массив на месте:
    numbers.sort(<)

    Это избегает создания копии массива.

  • Для пользовательских типов: Реализуйте протокол Comparable:
    struct Person: Comparable {
        let name: String
        let age: Int
        
        static func < (lhs: Person, rhs: Person) -> Bool {
            return lhs.age < rhs.age
        }
    }

    Тогда можно сортировать просто: people.sort()

Почему это работает?

В Swift Beta компилятор не всегда мог оптимизировать замыкания, особенно если они захватывали внешние переменные. Передача оператора < напрямую позволяет компилятору:

  • Инлайнить вызов сравнения
  • Избежать создания обертки для замыкания
  • Лучше оптимизировать циклы сортировки

В более поздних версиях Swift (начиная с Xcode 7) эта проблема была исправлена, и оба варианта работают одинаково быстро. Но в Xcode 6 Beta это было серьезной проблемой.

<<>>

Альтернативный подход: ручная сортировка для критичных участков

Если вы столкнулись с медленной работой sorted() в бета-версии и не хотите менять код, можно реализовать свою быструю сортировку. Например, используйте sort() из стандартной библиотеки C через qsort:

import Darwin

var numbers = [Int]()
// заполнение массива...

numbers.withUnsafeMutableBufferPointer { buffer in
    qsort(buffer.baseAddress, buffer.count, MemoryLayout<Int>.size) { a, b in
        let aValue = a.load(as: Int.self)
        let bValue = b.load(as: Int.self)
        if aValue < bValue { return -1 }
        if aValue > bValue { return 1 }
        return 0
    }
}

Этот подход использует нативную C-функцию, которая работает очень быстро. Однако он менее безопасен и не рекомендуется для production-кода. Лучше дождаться стабильной версии Swift или использовать sorted(by: <) как в первом ответе.

Также имейте в виду, что в бета-версиях часто встречаются проблемы с производительностью, которые исправляются к релизу. Поэтому не стоит паниковать — просто используйте обходные пути до выхода финальной версии.