Исправлена ​​предикатная производительность NSFetchedResultsController / NSFetchRequest с бэкэндом SQLite? - PullRequest
3 голосов
/ 04 февраля 2010

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

Я исследовал и оказалось, что предикат является проблемой:

NSPredicate *somePredicate = [NSPredicate predicateWithFormat:@"ANY somethings == %@", something];
[fetchRequest setPredicate:somePredicate];

Т.е. объект извлечения, называемый "вещами", имеет отношение "многие ко многим" с сущностью "нечто". Этот предикат является фильтром, который ограничивает результаты только вещами, имеющими отношение к определенному «чему-то».

Когда я удалил предикат для тестирования, время выборки (начальный executeFetch: вызов) уменьшилось (для некоторых крайних случаев) с 4 секунд до 100 мс или меньше, что является приемлемым. Однако меня это беспокоит, поскольку это сводит на нет многие преимущества, которые я надеялся получить с помощью Core Data и NSFRC, которые в остальном кажутся мощным инструментом.

Итак, мой вопрос, как я могу оптимизировать эту производительность? Я неправильно использую предикат? Должен ли я как-то изменить модель / схему? И какие еще есть способы это исправить? Можно ли ожидать такого снижения производительности? (Есть порядка сотен объектов размером <1 КБ.) </p>

РЕДАКТИРОВАТЬ С ДЕТАЛЯМИ:

Вот код:

[fetchRequest setFetchLimit:200];
NSLog(@"before fetch");
BOOL success = [frc performFetch:&error];
if (!success) {
    NSLog(@"Fetch request error: %@", error);
}
NSLog(@"after fetch");

Обновленные журналы (ранее у меня были некоторые неэффективности приложений, снижающие производительность. Это обновленные журналы, которые должны быть настолько близки к оптимальным, насколько это возможно в моей текущей среде):

2010-02-05 12:45:22.138 Special Ppl[429:207] before fetch
2010-02-05 12:45:22.144 Special Ppl[429:207] CoreData: sql: SELECT DISTINCT 0, t0.Z_PK, t0.Z_OPT, <model fields> FROM ZTHING t0 LEFT OUTER JOIN Z_1THINGS t1 ON t0.Z_PK = t1.Z_2THINGS WHERE  t1.Z_1SOMETHINGS = ? ORDER BY t0.ZID DESC LIMIT 200
2010-02-05 12:45:22.663 Special Ppl[429:207] CoreData: annotation: sql connection fetch time: 0.5094s
2010-02-05 12:45:22.668 Special Ppl[429:207] CoreData: annotation: total fetch execution time: 0.5240s for 198 rows.
2010-02-05 12:45:22.706 Special Ppl[429:207] after fetch

Если я сделаю то же самое без предиката (закомментировав две строки в начале вопроса):

2010-02-05 12:44:10.398 Special Ppl[414:207] before fetch
2010-02-05 12:44:10.405 Special Ppl[414:207] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, <model fields> FROM ZTHING t0 ORDER BY t0.ZID DESC LIMIT 200
2010-02-05 12:44:10.426 Special Ppl[414:207] CoreData: annotation: sql connection fetch time: 0.0125s
2010-02-05 12:44:10.431 Special Ppl[414:207] CoreData: annotation: total fetch execution time: 0.0262s for 200 rows.
2010-02-05 12:44:10.457 Special Ppl[414:207] after fetch

20-кратная разница во времени. 500 мс не так уж и велики, и, кажется, нет способа сделать это в фоновом режиме или оптимизировать каким-либо другим способом, о котором я могу думать. (Помимо перехода в бинарное хранилище, где это становится не проблема, так что я мог бы это сделать. Производительность бинарного хранилища постоянно составляет ~ 100 мс для вышеуказанного запроса с 200 объектами.)

(я вложил здесь еще один вопрос, который я сейчас удалил ).

Ответы [ 3 ]

4 голосов
/ 04 февраля 2010

Это постоянная проблема для многих из нас, пытающихся использовать Базовые Данные на iPhone для больших баз данных или более сложных схем.В моем профиле есть ссылки на несколько разных SO вопросов, связанных с этим - в контексте полнотекстового поиска.

Не все приложения работают медленно, просто применяя предикат к отношению ко многим, поэтому есть ещек этому.См. исполнительный документ Apple .Установите -com.apple.CoreData.SQLDebug 1.

Проверьте наличие индекса, если это необходимо.Убедитесь, что нет вызываемых функций sql (таких как преобразования форматов / типов), которые мешают sqlite эффективно объединяться или использовать индекс.

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

Если ваш предикат является более сложным, чем часть отношений со многими, вы можете поэкспериментировать споиск в два этапа.Например, SQL может неэффективно объединяться, а затем фильтроваться вместо того, чтобы фильтровать и затем объединяться.Но это нарушит NSFetchedResultsController.Тем не менее, это может пролить некоторый свет на проблему.

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

Поэкспериментируйте с удалением или добавлением обратной связи.В одном случае я обнаружил, что мой запрос выполняется намного быстрее без обратной связи.Недостатком было то, что удаление обратной связи раздуло мою базу данных.

Дайте нам знать, что вы найдете.

0 голосов
/ 31 марта 2010

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

Поскольку ваши отношения двусторонние ( правильно ?), Вы можете прийти к нему с другой стороны, и вам, вероятно, не нужен NSFetchedResultsController вообще для этой ситуации.Предположим, у вас есть:

MyWidgetEntity <--- >> SomethingEntity

Поскольку у вас уже есть ссылка на экземпляр SomethingEntity, вы просто запрашиваете его для виджета через:

id widget = [mySomethingInstance valueForKey:@"widget"];

В ситуации, когда у вас есть:

MyWidgetEntity << - >> SomethingEntity

Вы можете получить доступ ко всем связанным виджетам с помощью:

NSSet *widgets = [mySomethingInstance valueForKey:@"widgets"];

Нет NSFetchedResultsController необходимо, потому что вы можете превратить этот набор в массив и отсортировать его.

0 голосов
/ 31 марта 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...