2011-09-01 16 views
7

Moja aplikacja na telefon iPhone ma jednostkę Words o atrybutach word, length i language. Oba są indeksowane: Entity and attributesPobieranie prostych danych rdzenia jest bardzo powolne.

Skopiowałem cdatamodel i bazę danych do oddzielnej aplikacji importera, w której wstępnie wypełniono około 400 000 słów w różnych językach. Sprawdziłem import, zaglądając do pliku SQLite, a następnie skopiowałem wstępnie wypełnioną bazę danych do projektu iPhone'a.

Najpierw myślałem, że (prosty) predykat jest problemem. Ale nawet po usunięciu orzecznik z żądania zwrcania, trwa bardzo długo do wykonania:

2011-09-01 09:26:38.945 MyApp[3474:3c07] Start 
2011-09-01 09:26:58.120 MyApp[3474:3c07] End 

Oto co mój kod wygląda następująco:

// Get word 
NSLog(@"Start"); 
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Words" inManagedObjectContext:appDelegate.managedObjectContext]; 
[fetchRequest setEntity:entity]; 

NSError *error = nil; 
NSArray *fetchedObjects = [appDelegate.managedObjectContext executeFetchRequest:fetchRequest error:&error]; 
if (fetchedObjects == nil) { 
    //... error handling code 
} 

[fetchRequest release]; 
NSLog(@"End"); 
return fetchedObjects; 

Czy liczba wpisów w bazie danych problem z Core Data?


EDIT: Jak gcbrueckmann i jrturton wskazał, że to dobry punkt, aby ustawić fetchBatchSize. Ale sprowadzić czas nadal jest niezadowalająca:

  • 2 sekundy z kompletem kwantyfikatorów:

    NSPredicate * orzecznik = [NSPredicate predicateWithFormat: @ "długość ==% D i język BEGINSWITH% @", długość słowa, LNG ]; [fetchRequest setPredicate: predicate];

  • 7 sekund stosując zestaw wielkość wsadu

    [fetchRequest setFetchBatchSize: 1];

  • 1 drugim miejscu z obu orzecznika i wielkości partii ustawić

Czy istnieje jeszcze inna gardłem?

+0

w orzecznika jest językiem prawdopodobnie bardziej restrykcyjne niż długość, czasami kolejność kontroli źródłowych można przyspieszyć, jak również. na przykład w tym przypadku, jeśli 60% słów spełnia kryteria długości, ale tylko 40% spełnia kryteria językowe, lepiej najpierw sprawdzić język. Inną rzeczą może być, jeśli potrzebujesz tego szybciej byłoby mieć to wstępnie załadowane, a następnie filtr tablicy w pamięci, nie wiesz, czy twoja aplikacja iPhone może sobie z tym poradzić. –

+0

W tym przypadku pierwsze zapytanie porównuje liczby całkowite (indeksowanie spowoduje to bardzo szybko), a drugie jest porównaniem ciągów znaków (nawet indeksowany ciąg nie będzie szybki) - Byłbym zaskoczony, gdyby zmiana kolejności pomogła w ponownym uporządkowaniu zapytania. Jednak spróbuj - chciałbym zobaczyć, czy to pomogło! – deanWombourne

+0

Och, zapomniałem o tym wspomnieć: już próbowałem zamienić rozkaz predykatu, nie przyspiesza to pobierania. – Norbert

Odpowiedz

11

Ponieważ nie ograniczasz zestawu wyników w jakikolwiek sposób, pobieranie 400 000 obiektów na raz zdecydowanie będzie obciążeniem dla danych podstawowych. Istnieje kilka sposobów na poprawienie wydajności:

Zmiana żądania pobierania fetchBatchSize ogranicza liczbę obiektów, które będą przechowywane w pamięci w danym momencie. Ta funkcja jest całkowicie przezroczysta dla aplikacji, więc zdecydowanie warto spróbować.

Jeśli nie potrzebujesz pełnoprawnych obiektów, możesz rozważyć zmianę żądania pobierania na resultType na bardziej odpowiednią wartość. Zwłaszcza jeśli interesują Cię tylko niektóre wartości obiektu, dobrym pomysłem jest użycie NSDictionaryResultType.

Na koniec właściwości fetchLimit i fetchOffset pozwalają ograniczyć zakres wyników, jeśli chcesz samodzielnie zarządzać przetwarzaniem wsadowym. Jest to dobry pomysł, jeśli obsługa każdego z obiektów wynikowych używa dużej ilości pamięci, ponieważ można zawijać każdą partię w postaci NSAutoreleasePool (po prostu nie ulegaj pokusie tworzenia puli autoreas dla każdego obiektu wynikowego).

Chyba 1 sek. może być tak szybka, jak w twoim przypadku - nawet jeśli korzystasz z prostej bazy danych Sqlite. Jedyną dalszą optymalizacją, jaką mogę wymyślić, jest użycie jednej tabeli dla każdego języka (zamiast umieszczania słów ze wszystkich języków w jednej tabeli). To oczywiście będzie działać tylko z Sqlite, chyba że zdefiniujesz oddzielne elementy dla wszystkich języków, i. mi. weź swoją jednostkę Words tak, jak jest, i spraw, aby była abstrakcyjna. Następnie dodaj subwencje, takie jak EnglishWord itd. Obiekty z różnych podmiotów są przechowywane w oddzielnych tabelach. Tak więc, w połączeniu z parametrami fetchBatchSize i predicate, powinno to działać podobnie do podejścia Sqlite z oddzielnymi tabelami dla wszystkich języków.

+0

'fetchBatchSize' jest zdecydowanie dobrym punktem. Ale niestety trwa to 2 sekundy, aby zająć jedno słowo. – Norbert

+0

Czy korzystasz z bazy danych Sqlite o gołych kościach w swojej sprawie? Wygląda na to, że istniejące obiekty nie są modyfikowane, więc Core Data prawdopodobnie nie będzie miała (m) żadnych zalet w stosunku do zwykłego Sqlite. 400 000 to naprawdę duży zbiór danych na iPhone'a. Czy byłoby możliwe posiadanie jednego stołu na język? – gcbrueckmann

+0

Tak, już myślałem o przejściu na zwykły SQLite, ale myślałem, że nadal może być wąskie gardło, którego nie widzę. – Norbert

1

To by sprowadzić swój pełny 400k bazy danych do pamięci, która nie wydaje się dużo. Można badać

setFetchBatchSize 

metodę NSFetchRequest, który zatrzymuje ramy powracającego pełne przedmiotów za wszystko w swoim zwrcania żądanie, przy założeniu, że nie trzeba za każdym zwrócony obiekt zostać pobrane ze sklepu w pierwszej instancji.

2

Robisz BEGINSWITH - to nie jest bardzo szybka operacja! Jednak istnieje skończona liczba języków, więc emum prawdopodobnie pomógłby.

mieć pole language_id który jest indeksowany całkowitą i użyć jej w swojej orzecznika. Nadal można zapisać nazwę języka, jak również i zwrócić go jako część pobranego obiektu, tylko nie szukaj na nim :)


PS Można włączyć SQL debugowania przez dodanie „-com.apple. CoreData.SQLDebug 1 'jako argument przekazany przy uruchomieniu (skonfiguruj to w swoim Scheme) - to może pomóc ci zorientować się, co SQL wykonuje za kulisami.

(patrz this question więcej szczegółów)

+0

'język BEGINSWITH%' 'trwa 600ms (śr.); 'language ==% @' trwało 350ms (średnio)! – Norbert

+1

Wierzę, że wzdłuż tych linii widziałem, jak to się mówi, że porównywanie języka> =% @ jest szybsze niż używanie BEGINSWITH. Chcę powiedzieć, że było to w filmach z najważniejszymi danymi z WWDC z 2010 roku. –

+0

Jeśli to nadal jest równość łańcuchowa, to uzyskasz jeszcze większe przyspieszenie, jeśli konwertujesz do porównywania liczb całkowitych;) Indeksowanie ciągów w SQL uwzględnia tylko pewną liczbę znaków w łańcuchu, podczas gdy indeksowanie liczby całkowitej jest idealne! - http://dev.mysql.com/doc/refman/5.0/en/create-index.html – deanWombourne

Powiązane problemy