Самый быстрый способ найти firstIndex в большом массиве (1000) Swift

#arrays #swift #indexing #filter #instruments

#массивы #swift #индексирование #Фильтр #инструменты

Вопрос:

Есть ли более быстрый способ найти индекс в массиве пользовательских объектов?

У меня есть массив, количество которых составляет примерно 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)
}
 

SectionIndex

 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 (Swift 5)

Комментарии:

1. Если что-то занимает много времени, не делайте этого в основном потоке во время запуска. Запуск должен быть быстрым.

2. Как часто это array.firstIndex вызывается? 3,9 секунды кажутся слишком большим временем, если вы не вызываете его очень часто. Кроме того, вы уверены, что свойства SectionIndex должны быть необязательными? Судя init по тому, что вы показываете, вы можете избавиться от всех этих ? меток.

3. Да, несколько секунд только для 1000 элементов немыслимо. Происходит что-то еще. (Кстати, я предполагаю, что вы профилируете релизные / оптимизированные сборки, верно?)

4. Есть ли какие-то вложенные циклы или что-то в этом роде в вашем реальном коде. Невозможно, чтобы один поиск занял столько времени, но мне интересно, делаете ли вы это неоднократно. Отвечая на ваш вопрос, способы ускорить его — улучшить поиск до O (1) (например, Используя словарь, ваши собственные хэш-таблицы и т. Д.). Но, похоже, у нас недостаточно данных, чтобы диагностировать, что происходит…

5. Если это занимает несколько секунд, почти всегда лучше (а) представить счетчик (например, a UIActivityIndicatorView ); (б) сделать это в фоновом режиме; и (в) удалить счетчик и обновить модель и пользовательский интерфейс в основном потоке, когда это будет сделано. Это позволяет пользователю знать, что приложение не заморожено, и позволяет избежать риска того, что процесс watchdog уничтожит приложение, не отвечающее на запросы. Очевидно, что если вы можете ускорить это, как у вас есть, это здорово. Но все, что занимает более нескольких миллисекунд, почти всегда должно выполняться в фоновом потоке. Но я рад, что вы нашли хорошее решение для медленного, блокирующего процесса.

Ответ №1:

В соответствии с предложениями комментариев и предложением словаря Роба, я адаптировал следующее, что решило мою проблему и значительно улучшило производительность!

  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 разделов, но если у кого-нибудь есть какие-либо дополнительные предложения и улучшения, дайте мне знать.