From f9878f4fa44bc2325aebe9fb8c5b4f60db039934 Mon Sep 17 00:00:00 2001 From: Jeffrey Han Date: Sun, 8 Mar 2015 03:05:01 -0400 Subject: [PATCH] More optimizations: don't load OsuFile array fields until needed. When cached, defer loading of timing points, breaks, and combo colors until directly before a game. Potentially cuts loading time by 50%, and saves a bit of memory. Other changes: - Don't store the default combo colors in the cache. - Removed some PreparedStatements that only get executed once. Signed-off-by: Jeffrey Han --- src/itdelatrisu/opsu/OsuFile.java | 8 ++-- src/itdelatrisu/opsu/OsuParser.java | 6 ++- src/itdelatrisu/opsu/db/OsuDB.java | 57 ++++++++++++++++------- src/itdelatrisu/opsu/states/SongMenu.java | 5 ++ 4 files changed, 54 insertions(+), 22 deletions(-) diff --git a/src/itdelatrisu/opsu/OsuFile.java b/src/itdelatrisu/opsu/OsuFile.java index a8c09f57..42887582 100644 --- a/src/itdelatrisu/opsu/OsuFile.java +++ b/src/itdelatrisu/opsu/OsuFile.java @@ -165,7 +165,7 @@ public class OsuFile implements Comparable { */ /** All timing points. */ - public ArrayList timingPoints = new ArrayList(); + public ArrayList timingPoints; /** Song BPM range. */ public int bpmMin = 0, bpmMax = 0; @@ -374,6 +374,7 @@ public class OsuFile implements Comparable { * @param s the string */ public void timingPointsFromString(String s) { + this.timingPoints = new ArrayList(); if (s == null) return; @@ -390,10 +391,10 @@ public class OsuFile implements Comparable { /** * Returns the {@link #combo} field formatted as a string, - * or null if the field is null. + * or null if the field is null or the default combo. */ public String comboToString() { - if (combo == null) + if (combo == null || combo == Utils.DEFAULT_COMBO) return null; StringBuilder sb = new StringBuilder(); @@ -416,6 +417,7 @@ public class OsuFile implements Comparable { * @param s the string */ public void comboFromString(String s) { + this.combo = Utils.DEFAULT_COMBO; if (s == null) return; diff --git a/src/itdelatrisu/opsu/OsuParser.java b/src/itdelatrisu/opsu/OsuParser.java index f82c58bf..81e41e2d 100644 --- a/src/itdelatrisu/opsu/OsuParser.java +++ b/src/itdelatrisu/opsu/OsuParser.java @@ -163,7 +163,10 @@ public class OsuParser { // load cached entries from database if (!cachedOsuFiles.isEmpty()) { status = Status.CACHE; - OsuDB.load(cachedOsuFiles); + + // Load array fields only when needed to save time/memory. + // Change flag to 'LOAD_ALL' to load them immediately. + OsuDB.load(cachedOsuFiles, OsuDB.LOAD_NONARRAY); } // add group entries to OsuGroupList @@ -198,6 +201,7 @@ public class OsuParser { */ private static OsuFile parseFile(File file, File dir, ArrayList osuFiles, boolean parseObjects) { OsuFile osu = new OsuFile(file); + osu.timingPoints = new ArrayList(); try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) { String line = in.readLine(); diff --git a/src/itdelatrisu/opsu/db/OsuDB.java b/src/itdelatrisu/opsu/db/OsuDB.java index a46931fa..c0c4e84b 100644 --- a/src/itdelatrisu/opsu/db/OsuDB.java +++ b/src/itdelatrisu/opsu/db/OsuDB.java @@ -49,11 +49,14 @@ public class OsuDB { /** Minimum batch size to invoke batch insertion. */ private static final int INSERT_BATCH_MIN = 100; + /** OsuFile loading flags. */ + public static final int LOAD_NONARRAY = 1, LOAD_ARRAY = 2, LOAD_ALL = 3; + /** Database connection. */ private static Connection connection; /** Query statements. */ - private static PreparedStatement insertStmt, selectStmt, selectAllStmt, lastModStmt, deleteMapStmt, deleteGroupStmt; + private static PreparedStatement insertStmt, selectStmt, deleteMapStmt, deleteGroupStmt; // This class should not be instantiated. private OsuDB() {} @@ -80,8 +83,6 @@ public class OsuDB { "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, " + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); - lastModStmt = connection.prepareStatement("SELECT dir, file, lastModified FROM beatmaps"); - selectAllStmt = connection.prepareStatement("SELECT * FROM beatmaps"); selectStmt = connection.prepareStatement("SELECT * FROM beatmaps WHERE dir = ? AND file = ?"); deleteMapStmt = connection.prepareStatement("DELETE FROM beatmaps WHERE dir = ? AND file = ?"); deleteGroupStmt = connection.prepareStatement("DELETE FROM beatmaps WHERE dir = ?"); @@ -277,8 +278,10 @@ public class OsuDB { /** * Loads OsuFile fields from the database. * @param osu the OsuFile object + * @param flag whether to load all fields (LOAD_ALL), non-array + * fields (LOAD_NONARRAY), or array fields (LOAD_ARRAY) */ - public static void load(OsuFile osu) { + public static void load(OsuFile osu, int flag) { if (connection == null) return; @@ -286,8 +289,12 @@ public class OsuDB { selectStmt.setString(1, osu.getFile().getParentFile().getName()); selectStmt.setString(2, osu.getFile().getName()); ResultSet rs = selectStmt.executeQuery(); - if (rs.next()) - setOsuFileFields(rs, osu); + if (rs.next()) { + if ((flag & LOAD_NONARRAY) > 0) + setOsuFileFields(rs, osu); + if ((flag & LOAD_ARRAY) > 0) + setOsuFileArrayFields(rs, osu); + } rs.close(); } catch (SQLException e) { ErrorHandler.error("Failed to load OsuFile from database.", e, true); @@ -297,8 +304,10 @@ public class OsuDB { /** * Loads OsuFile fields from the database in a batch. * @param batch a list of OsuFile objects + * @param flag whether to load all fields (LOAD_ALL), non-array + * fields (LOAD_NONARRAY), or array fields (LOAD_ARRAY) */ - public static void load(List batch) { + public static void load(List batch, int flag) { if (connection == null) return; @@ -306,11 +315,11 @@ public class OsuDB { int size = batch.size(); if (size < LOAD_BATCH_MIN) { for (OsuFile osu : batch) - load(osu); + load(osu, flag); return; } - try { + try (Statement stmt = connection.createStatement()) { // create map HashMap> map = new HashMap>(); for (OsuFile osu : batch) { @@ -326,8 +335,9 @@ public class OsuDB { // iterate through database to load OsuFiles int count = 0; - selectAllStmt.setFetchSize(100); - ResultSet rs = selectAllStmt.executeQuery(); + stmt.setFetchSize(100); + String sql = "SELECT * FROM beatmaps"; + ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { String parent = rs.getString(1); HashMap m = map.get(parent); @@ -335,7 +345,10 @@ public class OsuDB { String name = rs.getString(2); OsuFile osu = m.get(name); if (osu != null) { - setOsuFileFields(rs, osu); + if ((flag & LOAD_NONARRAY) > 0) + setOsuFileFields(rs, osu); + if ((flag & LOAD_ARRAY) > 0) + setOsuFileArrayFields(rs, osu); if (++count >= size) break; } @@ -348,7 +361,7 @@ public class OsuDB { } /** - * Sets all OsuFile fields using a given result set. + * Sets all OsuFile non-array fields using a given result set. * @param rs the result set containing the fields * @param osu the OsuFile * @throws SQLException @@ -387,6 +400,15 @@ public class OsuDB { osu.widescreenStoryboard = rs.getBoolean(34); osu.epilepsyWarning = rs.getBoolean(35); osu.bg = OsuParser.getDBString(rs.getString(36)); + } + + /** + * Sets all OsuFile array fields using a given result set. + * @param rs the result set containing the fields + * @param osu the OsuFile + * @throws SQLException + */ + private static void setOsuFileArrayFields(ResultSet rs, OsuFile osu) throws SQLException { osu.timingPointsFromString(rs.getString(37)); osu.breaksFromString(rs.getString(38)); osu.comboFromString(rs.getString(39)); @@ -400,10 +422,11 @@ public class OsuDB { if (connection == null) return null; - try { + try (Statement stmt = connection.createStatement()) { Map map = new HashMap(); - ResultSet rs = lastModStmt.executeQuery(); - lastModStmt.setFetchSize(100); + String sql = "SELECT dir, file, lastModified FROM beatmaps"; + ResultSet rs = stmt.executeQuery(sql); + stmt.setFetchSize(100); while (rs.next()) { String path = String.format("%s/%s", rs.getString(1), rs.getString(2)); long lastModified = rs.getLong(3); @@ -460,9 +483,7 @@ public class OsuDB { try { insertStmt.close(); - lastModStmt.close(); selectStmt.close(); - selectAllStmt.close(); deleteMapStmt.close(); deleteGroupStmt.close(); connection.close(); diff --git a/src/itdelatrisu/opsu/states/SongMenu.java b/src/itdelatrisu/opsu/states/SongMenu.java index f203ee8b..8a337c55 100644 --- a/src/itdelatrisu/opsu/states/SongMenu.java +++ b/src/itdelatrisu/opsu/states/SongMenu.java @@ -1284,8 +1284,13 @@ public class SongMenu extends BasicGameState { SoundController.playSound(SoundEffect.MENUHIT); OsuFile osu = MusicController.getOsuFile(); Display.setTitle(String.format("%s - %s", game.getTitle(), osu.toString())); + + // load any missing data + if (osu.timingPoints == null || osu.combo == null) + OsuDB.load(osu, OsuDB.LOAD_ARRAY); OsuParser.parseHitObjects(osu); HitSound.setDefaultSampleSet(osu.sampleSet); + MultiClip.destroyExtraClips(); ((Game) game.getState(Opsu.STATE_GAME)).setRestart(Game.Restart.NEW); game.enterState(Opsu.STATE_GAME, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));