Есть ли более быстрый способ найти индекс в массиве пользовательских объектов?
У меня есть массив, который имеет количество около 1000. И в каждом SectionIndex есть MainIndex со всеми моими свойствами, каждый SectionIndex имеет примерно 15 частей MainIndex. SectionIndex используется для заголовков разделов TableView, а часть .data[MainIndex] заполняет строки в каждом разделе.
После запуска инструментов (профилирование времени) из 5 секунд, необходимых для запуска моего приложения, строка (array.firstIndex) заняла 3,89 секунды, могу ли я как-то ускорить ту часть, где я нахожу индекс?
var array: [SectionIndex] = [SectionIndex]()
Основная часть в функции:
let title = "\(dateMonth), \(dateYear)"
if let offset = array.firstIndex(where: { $0.title == title })
{
array[offset].data.append(insert)
if let mins = array[offset].minutes {
array[offset].minutes = mins + timeToMinutes(minutes)
}
}
else
{
let insert2 = SectionIndex(index: array.count, title: title, date: date, data: [insert], minutes: timeToMinutes(minutes))
array.append(insert2)
}
Индекс раздела
class SectionIndex {
let index: Int?
let title: String?
let date: Date?
var data: [MainIndex] = [MainIndex]()
var minutes: Int?
init(index: Int, title: String, date: Date, data: [MainIndex], minutes: Int)
{
self.index = index
self.title = title
self.date = date
self.data = data
self.minutes = minutes
}
}
Причина, по которой мне нужно найти или добавить индекс:
Мои данные относятся к разным месяцам года примерно за 1000 месяцев, всякий раз, когда месяц и год найдены первыми, они становятся заголовком, в следующий раз, когда он уже существует, я добавляю данные в этот раздел.
Xcode 12.2 (Свифт 5)
Как часто это array.firstIndex
называется? 3,9 секунды кажутся слишком большим временем, если только вы не вызываете его очень часто. Кроме того, вы уверены, что свойства в SectionIndex
должны быть необязательными? Судя по тому, что вы показываете init
, вы можете избавиться от всех этих ?
меток.
Да, несколько секунд для всего 1000 элементов немыслимы. Происходит что-то еще. (Кстати, я предполагаю, что вы профилируете выпуск/оптимизированные сборки, верно?)
Есть ли какие-то вложенные циклы или что-то в этом роде в вашем реальном коде. Не может быть, чтобы один поиск занял так много времени, но мне интересно, делаете ли вы это постоянно. Отвечая на ваш вопрос, способы его ускорения заключаются в том, чтобы улучшить поиск до O (1) (например, с использованием словаря, ваших собственных хеш-таблиц и т. д.). Но не похоже, чтобы у нас было достаточно, чтобы диагностировать, что происходит...
Да, в основном, так как мне нужно добавить 1000 разделов и 15 записей в каждом разделе. Всего в базе данных 15 000 записей, поэтому эта строка выполняется 15 000 раз, чтобы проверить, существует ли заголовок или его нужно добавить.
Причина, по которой он находится в основном потоке при запуске, заключается в том, что это основные данные, которые должен видеть пользователь. Только это медленно, когда пользователь достигает 15 000, что является максимальным. Гораздо быстрее, когда их меньше. Но я тестирую наихудший сценарий, чтобы иметь минимальное время загрузки. Что касается опционов, это правда, что на самом деле сэкономит еще долю секунды на разворачивании большого количества опционов.
Если это занимает несколько секунд, почти всегда лучше (а) подарить спиннер (например, UIActivityIndicatorView
); (б) делать это в фоновом режиме; и (c) удалить счетчик и обновить модель и пользовательский интерфейс в основном потоке, когда это будет сделано. Это позволяет пользователю узнать, что приложение не зависло, и позволяет избежать риска того, что процесс сторожевого таймера уничтожит не отвечающее приложение. Понятно, что если вы можете ускорить его, как у вас, это здорово. Но все, что занимает больше нескольких миллисекунд, почти всегда должно выполняться в фоновом потоке. Но я рад, что вы нашли хорошее решение для медленного, блокирующего процесса.
Да, я понимаю, что вы имеете в виду, я изначально думал об анимации загрузки, но проблема в том, что когда количество введенных данных ниже 6000 или iPhone XS Max, анимация будет появляться и исчезать, поскольку это происходит быстро. Возникает только проблема, которая будет редкостью, когда пользователь начнет достигать максимальных данных. Я думаю, у меня могут быть оба варианта в зависимости от времени загрузки? Как только время функции превысит пороговое значение, я могу сохранить переменную медленной загрузки, которая будет запускать анимацию загрузки при каждом запуске. Кстати, спасибо за вашу помощь, время, которое я сэкономил на загрузке, потрясающе, просто с использованием словаря.
В соответствии с предложениями комментариев и предложением словаря Роба я адаптировал следующее, что решило мою проблему и значительно улучшило производительность!
var currentIndex: Int = 0
var dic: [String: Int] = [:]
// Code below runs 15,000 times for each database entry
if let offset = dic[title]
{
array[offset].data.append(insert)
if let mins = array[offset].minutes { array[offset].minutes = mins + timeToMinutes(minutes) }
}
else
{
let insert2 = SectionIndex(index: currentIndex, title: title, date: date, data: [insert], minutes: timeToMinutes(minutes))
dic[title] = currentIndex
currentIndex += 1
array.append(insert2)
}
Я также избавился от опций в моей основной части с большим количеством записей, что еще больше улучшило производительность загрузки. Результаты с вышеизложенным:
Основная тема: 1,91 с
Старт: 1,77 с
Инициализация системного интерфейса: 1,29 с
Инициализация UIKit: 933 мс
Запуск начального рендеринга кадра: 1,64 с
Я доволен результатом, так как это наихудший случай загрузки 15 000 записей в 1000 разделов, но если у кого-то есть какие-либо дополнительные предложения и улучшения, дайте мне знать.
Если что-то занимает много времени, не делайте этого в основном потоке во время запуска. Запуск должен быть быстрым.