#swift #macos
Вопрос:
Мне нужно было пройти большую часть (более 500 тыс. файлов) дерева файлов и переключиться с передачи FileManager.default.enumerator()
a String
на a URL
. Ход становится в 3 раза быстрее, и я пытаюсь понять, почему.
Я тестирую на своем Mac с диском в формате APFS.
Это мой тестовый код для измерения этого на игровой площадке Swift:
import Cocoa
var startingTime: Date
var pathCount = 0
var urlCount = 0
let path = "/Users/tom/myfolder/"
let pathEnumerator = FileManager.default.enumerator(atPath: path)
let url = URL(fileURLWithPath: path)
let urlEnumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: nil)
print("== URL Enumerator ==")
startingTime = Date()
while let _ = urlEnumerator?.nextObject() as? URL {
urlCount = 1
}
print("n(urlCount) files.")
print("(startingTime.timeIntervalSinceNow * -1) seconds elapsed")
print("nn")
print("== Path/String Enumerator ==")
startingTime = Date()
while let _ = pathEnumerator?.nextObject() as? String {
pathCount = 1
}
print("n(pathCount) files.")
print("(startingTime.timeIntervalSinceNow * -1) seconds elapsed")
Это результат, который я получаю:
== URL Enumerator ==
541879 files.
40.580654978752136 seconds elapsed
== Path/String Enumerator ==
541879 files.
118.60869300365448 seconds elapsed
Если я изменю порядок ( String
сначала сделаю версию), это не имеет значения, поэтому это не похоже на артефакт кэширования.
Комментарии:
1.Используемые API-интерфейсы
URL
являются более новыми, и вам рекомендуется их использовать, в то время как те, которые принимаютString
s, должны быть мягко устаревшими. Так что разница в производительности не слишком удивительна.2. @Sweeper — Да,
URL
API-интерфейсы на основе более новые, но это все равно большая разница, особенно после того, как вы инициализировали путь, работа, которую должна выполнять функция, не должна слишком отличаться.3. Вы пробовали «Профилировщик времени»инструментов?
4. Вы должны проводить тесты производительности в скомпилированном проекте, а не на игровой площадке.
5. @MartinR — Я только что попробовал это, и это все еще >в 2 раза быстрее, чем скомпилированный инструмент командной строки. Однако в целом они оба намного быстрее (11 секунд против 22), что было для меня неожиданностью. Это было полезно, так как я был одержим оптимизацией, когда она, возможно, вообще не нужна.
Ответ №1:
Благодаря предложению @Willeke я запустил это с помощью инструмента Xcode «Профилировщик времени» и просмотрел стек вызовов для каждого пути. Это высветило разницу в поведении между этими двумя методами.
Стек вызовов для URL
подхода:
Стек вызовов для String
подхода:
Моя лучшая интерпретация этого заключается в том, что более старый String
API представляет собой рекурсивный обход дерева, который сопряжен с большими накладными расходами на производительность, в то время как более новый URL
API «знает» дерево файлов и может просто итеративно его обходить.
@Martin R предложил мне проверить это в скомпилированном приложении (откуда взяты скриншоты). Разница все еще в 2 раза, но они оба были намного быстрее (11 против 22), чем на игровой площадке.