Производительность NSManagedObjectContext save значительно снижается - PullRequest
8 голосов
/ 16 июня 2011

У меня проблемы с iOS-приложением на основе CoreData, когда оно пытается создать исходную БД из данных, отправленных с сервера.По сути, сервер отправляет 1 МБ фрагментов объектов (около 3000 на блок), и клиент iOS десериализует их и записывает их на диск.

Я вижу, что все идет хорошосначала 8 блоков (из 44), затем производительность резко падает, и каждый блок начинает занимать все больше и больше времени, как на рисунке ниже.Практически все время расходуется на [NSManagedObjectContext save], как вы можете видеть в данных профилирования Instruments, но также кажется, что приложение больше не работает на 100% CPU по какой-то причине, например, оно ожидает дискового ввода-вывода иличто-то.

profiling data showing performance degradation

Несколько важных фактов о том, как я это делаю:

  • Каждый кусок обрабатывается в своем собственном NSManagedObjectContext со своим собственным NSAutoreleasePool, поэтому между обработкой фрагментов не происходит наращивание объекта в незагруженном контексте.

  • Не задано NSUndoManager для любого изконтексты.

  • Не происходит mergeChangesFromContextDidSaveNotification: (т. е. контексты чанков не переносят свои изменения в «главный» контекст)

  • Я использую хранилище данных на базе SQLite в iOS 4.3.

  • Записываемые записи имеют индексы.

  • вся синхронизация выполняется в одном фоновом потоке GCD (например, dispatch_queue_create() и dispatch_async()).

Понятия не имею, почему производительностьвнезапно падает, как это или что можно сделать для решения этой проблемы.Я осмотрелся и прочитал следующее, но ничего еще не выскочило на меня:

Любые идеи или указатели для того, чтобы сделать это приложение масштабируемым до 100 000 записей в базе данных, будут высоко оценены.

Редактировать - дополнительная статистика

Этот график инструментов показывает ту же симуляцию, что и выше (на iPad2), но включает в себя статистику активности диска, и вы можете довольно ясно увидеть, что все "не работает на 100"Время% CPU ", по-видимому, занято при записи на диск.

Disk activity for original test

Я также выполнил ту же попытку синхронизации, запущенную на симуляторе iOS.Общее использование памяти является более или менее постоянным для каждого чанка, за исключением словаря, который содержит идентификаторы объектов, которые со временем немного растут (но это не объекты CoreData или что-либо, что может повлиять на сохранение, это просто номера NSN).Это диктует небольшой объем памяти по сравнению с общей кучей, поэтому проблема не исчерпывается памятью.

Что интересно в этом тесте, так это то, что инструмент CoreData Save сообщает, что последующие сохранения занимают примерното же время, которое явно конфликтует с информацией о профилировании процессора из первого набора результатов.Похоже, что CoreData считает, что на внесение изменений в БД уходит столько же времени, но сама БД (т. Е. SQLite) внезапно занимает намного больше времени, чтобы фактически передать эти изменения на диск.

Ответы [ 2 ]

7 голосов
/ 18 мая 2013

Я знаю, что это старая проблема, поэтому она, вероятно, больше не актуальна для вас, но может быть для кого-то еще.

Я видел проблемы с производительностью при заполнении базы данных Core Data через iCloud и обнаружилчто если у вас есть обратные отношения с моделью данных, вы можете получить невероятно плохую производительность.Реализация ведения журнала транзакций iCloud на самом деле кажется неизбежной проблемой.Каждая транзакция, отправляемая в iCloud (посмотрите на developer.icloud.com - это просто заархивированные списки), записывает все отношения, на которые повлияло изменение.В отличие от того, когда вы изменяете один конец отношения в Базовых данных, и он заботится об обратном конце, журнал транзакций с базовыми данными заканчивает записывать изменения в обоих концах, а не обрабатывает их.

Таким образом, если у вас есть отношение 1 ко многим, и вы создаете другую запись, которая в конечном итоге свисает с конца «многие» - ну, запись на конце «1» также будет обновлена, чтобы отразить фактновая дополнительная запись теперь висит на нем.Если у вас есть архитектура, которая означает, что у вас есть объект типа, который блокирует множество объектов данных, то каждый раз, когда вы добавляете новый объект данных, для типа «тип» будет также записываться транзакция для него - новот что важно, потому что транзакции Core Data iCloud записывают ВЕСЬ состояние отредактированных объектов, а не только изменения, КАЖДЫЕ отношения, уже записанные против него, также добавляются в журнал, а не только тот, который указывает новую подчиненную запись.Это может быстро выйти из-под контроля, так как количество записываемых данных растет по мере роста количества взаимосвязей между сущностями - это приводит к тому, что для сохранения пакетов требуется все больше и больше времени.

Я ответил на вопрос примерно такдо здесь на форумах разработчиков Apple, которые могут оказаться полезными, поскольку я никогда не смогу описать это кратко.

Самый простой способ улучшить производительность высева, если этот сценарий влияет на васотключить обратные отношения, но это не всегда возможно.

1 голос
/ 17 июня 2011

Дополнительная информация о вашей реализации поможет.Например, вы запускаете это в основном потоке или вы используете фоновые потоки?Тем не менее, я видел это поведение раньше.При выполнении обширных пакетных операций с использованием Core Data, он может замедляться, если память не управляется должным образом.Вы проверили использование памяти?Вы проверили на утечки?Еще одна вещь, которую стоит попробовать - убедиться, что вы используете NSAutoreleasePool правильно, если это необходимо.Периодически сливая пул, это может повысить производительность.

...