So,
die Nacht hat sich gelohnt
Ich bin gestern so gegen 19:00 Uhr zum Sport, auf dem G1 liefen die Inserts weiter. Gegen 23:00 Uhr habe Ich wieder auf das Telefon geschaut, er machte Lustig weiter. Ups dachte Ich diese Geschwindigkeit hat Showstopper Qualität.
Danach habe Ich mir nochmals den "Insert Loop" angeschaut und nach "Performance Sünden" gesucht.
Lange rede kurzer Sinn, hier der Loop wie er vorher war:
1public void update()
2 {
3 clearAllSpots();
4 final SQLiteDatabase db = database.getWritableDatabase();
5 final SQLiteStatement insertSpotStatement = db.compileStatement(INSERT_SPOT);
6 final SQLiteStatement insertContinentStatement = db.compileStatement(INSERT_CONTINENT);
7 // db.beginTransaction();
8
9 Log.d(LOG_TAG, "executeInsert");
10
11 final long start = System.currentTimeMillis();
12 try
13 {
14 for (final Continent continent : Continent.values())
15 {
16 updateContinentTable(insertContinentStatement, continent);
17 insertContinentStatement.execute();
18
19 final Country[] countrys = continent.getCoutrys();
20 for (final Country country : countrys)
21 {
22 final Region[] regions = country.getRegions();
23 for (final Region region : regions)
24 {
25 final Station[] stations = region.getStations();
26 for (final Station station : stations)
27 {
28 /**
29 * "INSERT_SPOT INTO spot (spotid,continentid,regionid,name,keyword,superforecast,forecast,statistic,wavereport,waveforecast) "
30 * values (?,?,?,?,?,?,?,?,?,?);";
31 */
32 updateSpotTable(insertSpotStatement, continent, region, station);
33 insertSpotStatement.execute();
34
35 }
36 }
37 }
38 }
39 }
40 finally
41 {
42 // db.endTransaction();
43 insertSpotStatement.close();
44 IOUtils.close(db);
45 final long time = System.currentTimeMillis() - start;
46 Log.d(LOG_TAG, "Insert took " + time + " ms");
47 }
Zuerst habe Ich die insertXXX Methoden final static gesetzt, was wenig brachte, diese sahen vorher in etwa so aus:
1/**
2 * @param insertStatement
3 * @param continent
4 * @param region
5 * @param station
6 */
7 private void updateContinentTable(final SQLiteStatement insertStatement, final Continent continent)
8 {
9 insertStatement.bindString(1, continent.getId());
10 insertStatement.bindString(2, continent.name());
11 }
Dann fing Ich an den Innerloop zu optimieren, indem Ich die Methode updateSpotTable in den loop verschob. Der Performancegewinn war nicht messbar.
Interessanter waren da schon die Business Objekte Station, Region, Country und deren getXXXX Methoden:
1public Region[] getRegions()
2 {
3 return regions.toArray(new Region[regions.size()]);
4 }
Ich denke dazu muss man nicht so viel sagen, hier wird viel Garbage erzeugt, auch wenn es nach Lehrbuch ist.
Intern halte Ich ein Set in diesen Objekten Set<Region>, also habe Ich den Objekten eine iterator Methode spendiert:
1public Iterator<Region> iterator()
2 {
3 return regions.iterator();
4 }
Und die insert schleifen entsprechend angepasst. Dann fühlte ich erstmals einen Performancegewinn. Allerdings war das immer noch nicht gut genug.
Ich habe während der Inserts einen ProgressDialog laufen (das ist oben nicht zu sehen), dieser wird im Innerloop updated, daher habe Ich das einfach mal nicht getan. Ohh Wunder,im Emulator rannte das recht schnell also sollte es auf dem echten Gerät ebenfalls deutlich schneller gehen (Der Emulator ist wirklich nur einen wage Annäherung an die Geschwindigkeit des G1).
Auf dem echten G1 lief der Datenbank Insert nun deutlichst schneller, in ca. 1 Minute waren alle Datensätze eingearbeitet.
Nun ging es darum dem User dennoch etwas an Feedback zu geben damit er nicht denkt die Anwendung ist "abgeschmiert".
Daher ist im innerloop der Code folgendermassen verändert worden:
1while (stations.hasNext())
2 {
3 updateSpotTable(insertSpotStatement, continent, country, region, stations.next());
4 index++;
5 if (index % 100 == 0)
6 {
7 progress.incrementProgressBy(100);
8 }
9 }
Die Performance ist nun auf einem Level der als brauchbar anzusehen ist auf meinem G1. Ich habe also folgendes feststellen können: UI Update sollte man auf ein minimum reduzieren wenn möglich und nebenbei drauf achten das der GC nicht zuviel futter bekommt.
Der Performancegewinn ist mehr als beachtlich, da es anfangs mehrere Stunden dauerte, so das die App den Fokus verliert und auch weniger CPU Zyklen bereitgestellt bekommt was das ganze noch langsamer werden lässt.
Ich denke das Hauptproblem liegt darin das das UI zu oft zu refreshed wurde und dadurch das System mit Events zu "geflutet" wurde. Ähnliches kenne Ich aus Swing, nur das es hier schneller auftritt.
Hier nochmals das Endergebnis:
1public void update()
2 {
3 clearAllSpots();
4 final SQLiteDatabase db = database.getWritableDatabase();
5 final SQLiteStatement insertSpotStatement = db.compileStatement(INSERT_SPOT);
6 final SQLiteStatement insertContinentStatement = db.compileStatement(INSERT_CONTINENT);
7 final SQLiteStatement insertCountryStatement = db.compileStatement(INSERT_COUNTRY);
8 final SQLiteStatement insertRegionStatement = db.compileStatement(INSERT_REGION);
9
10 // db.beginTransaction();
11
12 if (!Continent.isParsed())
13 {
14 throw new IllegalStateException("Please parse Continents");
15 }
16
17 Log.d(LOG_TAG, "executeInsert");
18
19 final long start = System.currentTimeMillis();
20 int index = 0;
21 try
22 {
23 for (final Continent continent : Continent.values())
24 {
25 updateContinentTable(insertContinentStatement, continent);
26 insertContinentStatement.executeInsert();
27
28 final Iterator<Country> countrys = continent.iterator();
29 while (countrys.hasNext())
30 {
31 final Country country = countrys.next();
32 updateCountryTable(insertCountryStatement, country);
33 insertCountryStatement.executeInsert();
34
35 final Iterator<Region> regions = country.iterator();
36 while (regions.hasNext())
37 {
38 final Region region = regions.next();
39 updateRegionTable(insertRegionStatement, region);
40 insertRegionStatement.executeInsert();
41
42 final Iterator<Station> stations = region.iterator();
43 while (stations.hasNext())
44 {
45 updateSpotTable(insertSpotStatement, continent, country, region, stations.next());
46 index++;
47 if (index % 100 == 0)
48 {
49 progress.incrementBy(100);
50 }
51 }
52 }
53 }
54 }
55 }
56 finally
57 {
58 // db.endTransaction();
59 insertSpotStatement.close();
60 insertCountryStatement.close();
61 insertRegionStatement.close();
62 insertContinentStatement.close();
63
64 IOUtils.close(db);
65 final long time = System.currentTimeMillis() - start;
66 Log.d(LOG_TAG, "Insert took " + time + " ms");
67 }
68
69 }
Hoffe das hilft jemanden weiter,
Mac
PS: Nebenbei konnte Ich keine Unterschiede feststellen wenn Ich indiezies auf den Tabellen habe bei der Einarbeitung
Windmate HD, See you @ IO 14 , Worked on Wundercar, Glass V3, LG G Watch, Moto 360, Android TV
Empfohlener redaktioneller Inhalt
Mit Deiner Zustimmung wird hier ein externer Inhalt geladen.
Mit Klick auf den oben stehenden Button erklärst Du Dich damit einverstanden, dass Dir externe Inhalte angezeigt werden dürfen. Dabei können personenbezogene Daten an Drittanbieter übermittelt werden. Mehr Infos dazu findest Du in unserer Datenschutzerklärung.