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 <itdelatrisu@gmail.com>
This commit is contained in:
Jeffrey Han 2015-03-08 03:05:01 -04:00
parent f8e91cba64
commit f9878f4fa4
4 changed files with 54 additions and 22 deletions

View File

@ -165,7 +165,7 @@ public class OsuFile implements Comparable<OsuFile> {
*/ */
/** All timing points. */ /** All timing points. */
public ArrayList<OsuTimingPoint> timingPoints = new ArrayList<OsuTimingPoint>(); public ArrayList<OsuTimingPoint> timingPoints;
/** Song BPM range. */ /** Song BPM range. */
public int bpmMin = 0, bpmMax = 0; public int bpmMin = 0, bpmMax = 0;
@ -374,6 +374,7 @@ public class OsuFile implements Comparable<OsuFile> {
* @param s the string * @param s the string
*/ */
public void timingPointsFromString(String s) { public void timingPointsFromString(String s) {
this.timingPoints = new ArrayList<OsuTimingPoint>();
if (s == null) if (s == null)
return; return;
@ -390,10 +391,10 @@ public class OsuFile implements Comparable<OsuFile> {
/** /**
* Returns the {@link #combo} field formatted as a string, * 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() { public String comboToString() {
if (combo == null) if (combo == null || combo == Utils.DEFAULT_COMBO)
return null; return null;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@ -416,6 +417,7 @@ public class OsuFile implements Comparable<OsuFile> {
* @param s the string * @param s the string
*/ */
public void comboFromString(String s) { public void comboFromString(String s) {
this.combo = Utils.DEFAULT_COMBO;
if (s == null) if (s == null)
return; return;

View File

@ -163,7 +163,10 @@ public class OsuParser {
// load cached entries from database // load cached entries from database
if (!cachedOsuFiles.isEmpty()) { if (!cachedOsuFiles.isEmpty()) {
status = Status.CACHE; 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 // add group entries to OsuGroupList
@ -198,6 +201,7 @@ public class OsuParser {
*/ */
private static OsuFile parseFile(File file, File dir, ArrayList<OsuFile> osuFiles, boolean parseObjects) { private static OsuFile parseFile(File file, File dir, ArrayList<OsuFile> osuFiles, boolean parseObjects) {
OsuFile osu = new OsuFile(file); OsuFile osu = new OsuFile(file);
osu.timingPoints = new ArrayList<OsuTimingPoint>();
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) { try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) {
String line = in.readLine(); String line = in.readLine();

View File

@ -49,11 +49,14 @@ public class OsuDB {
/** Minimum batch size to invoke batch insertion. */ /** Minimum batch size to invoke batch insertion. */
private static final int INSERT_BATCH_MIN = 100; 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. */ /** Database connection. */
private static Connection connection; private static Connection connection;
/** Query statements. */ /** Query statements. */
private static PreparedStatement insertStmt, selectStmt, selectAllStmt, lastModStmt, deleteMapStmt, deleteGroupStmt; private static PreparedStatement insertStmt, selectStmt, deleteMapStmt, deleteGroupStmt;
// This class should not be instantiated. // This class should not be instantiated.
private OsuDB() {} 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 = ?"); selectStmt = connection.prepareStatement("SELECT * FROM beatmaps WHERE dir = ? AND file = ?");
deleteMapStmt = connection.prepareStatement("DELETE FROM beatmaps WHERE dir = ? AND file = ?"); deleteMapStmt = connection.prepareStatement("DELETE FROM beatmaps WHERE dir = ? AND file = ?");
deleteGroupStmt = connection.prepareStatement("DELETE FROM beatmaps WHERE dir = ?"); deleteGroupStmt = connection.prepareStatement("DELETE FROM beatmaps WHERE dir = ?");
@ -277,8 +278,10 @@ public class OsuDB {
/** /**
* Loads OsuFile fields from the database. * Loads OsuFile fields from the database.
* @param osu the OsuFile object * @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) if (connection == null)
return; return;
@ -286,8 +289,12 @@ public class OsuDB {
selectStmt.setString(1, osu.getFile().getParentFile().getName()); selectStmt.setString(1, osu.getFile().getParentFile().getName());
selectStmt.setString(2, osu.getFile().getName()); selectStmt.setString(2, osu.getFile().getName());
ResultSet rs = selectStmt.executeQuery(); ResultSet rs = selectStmt.executeQuery();
if (rs.next()) if (rs.next()) {
setOsuFileFields(rs, osu); if ((flag & LOAD_NONARRAY) > 0)
setOsuFileFields(rs, osu);
if ((flag & LOAD_ARRAY) > 0)
setOsuFileArrayFields(rs, osu);
}
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
ErrorHandler.error("Failed to load OsuFile from database.", e, true); 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. * Loads OsuFile fields from the database in a batch.
* @param batch a list of OsuFile objects * @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<OsuFile> batch) { public static void load(List<OsuFile> batch, int flag) {
if (connection == null) if (connection == null)
return; return;
@ -306,11 +315,11 @@ public class OsuDB {
int size = batch.size(); int size = batch.size();
if (size < LOAD_BATCH_MIN) { if (size < LOAD_BATCH_MIN) {
for (OsuFile osu : batch) for (OsuFile osu : batch)
load(osu); load(osu, flag);
return; return;
} }
try { try (Statement stmt = connection.createStatement()) {
// create map // create map
HashMap<String, HashMap<String, OsuFile>> map = new HashMap<String, HashMap<String, OsuFile>>(); HashMap<String, HashMap<String, OsuFile>> map = new HashMap<String, HashMap<String, OsuFile>>();
for (OsuFile osu : batch) { for (OsuFile osu : batch) {
@ -326,8 +335,9 @@ public class OsuDB {
// iterate through database to load OsuFiles // iterate through database to load OsuFiles
int count = 0; int count = 0;
selectAllStmt.setFetchSize(100); stmt.setFetchSize(100);
ResultSet rs = selectAllStmt.executeQuery(); String sql = "SELECT * FROM beatmaps";
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) { while (rs.next()) {
String parent = rs.getString(1); String parent = rs.getString(1);
HashMap<String, OsuFile> m = map.get(parent); HashMap<String, OsuFile> m = map.get(parent);
@ -335,7 +345,10 @@ public class OsuDB {
String name = rs.getString(2); String name = rs.getString(2);
OsuFile osu = m.get(name); OsuFile osu = m.get(name);
if (osu != null) { 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) if (++count >= size)
break; 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 rs the result set containing the fields
* @param osu the OsuFile * @param osu the OsuFile
* @throws SQLException * @throws SQLException
@ -387,6 +400,15 @@ public class OsuDB {
osu.widescreenStoryboard = rs.getBoolean(34); osu.widescreenStoryboard = rs.getBoolean(34);
osu.epilepsyWarning = rs.getBoolean(35); osu.epilepsyWarning = rs.getBoolean(35);
osu.bg = OsuParser.getDBString(rs.getString(36)); 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.timingPointsFromString(rs.getString(37));
osu.breaksFromString(rs.getString(38)); osu.breaksFromString(rs.getString(38));
osu.comboFromString(rs.getString(39)); osu.comboFromString(rs.getString(39));
@ -400,10 +422,11 @@ public class OsuDB {
if (connection == null) if (connection == null)
return null; return null;
try { try (Statement stmt = connection.createStatement()) {
Map<String, Long> map = new HashMap<String, Long>(); Map<String, Long> map = new HashMap<String, Long>();
ResultSet rs = lastModStmt.executeQuery(); String sql = "SELECT dir, file, lastModified FROM beatmaps";
lastModStmt.setFetchSize(100); ResultSet rs = stmt.executeQuery(sql);
stmt.setFetchSize(100);
while (rs.next()) { while (rs.next()) {
String path = String.format("%s/%s", rs.getString(1), rs.getString(2)); String path = String.format("%s/%s", rs.getString(1), rs.getString(2));
long lastModified = rs.getLong(3); long lastModified = rs.getLong(3);
@ -460,9 +483,7 @@ public class OsuDB {
try { try {
insertStmt.close(); insertStmt.close();
lastModStmt.close();
selectStmt.close(); selectStmt.close();
selectAllStmt.close();
deleteMapStmt.close(); deleteMapStmt.close();
deleteGroupStmt.close(); deleteGroupStmt.close();
connection.close(); connection.close();

View File

@ -1284,8 +1284,13 @@ public class SongMenu extends BasicGameState {
SoundController.playSound(SoundEffect.MENUHIT); SoundController.playSound(SoundEffect.MENUHIT);
OsuFile osu = MusicController.getOsuFile(); OsuFile osu = MusicController.getOsuFile();
Display.setTitle(String.format("%s - %s", game.getTitle(), osu.toString())); 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); OsuParser.parseHitObjects(osu);
HitSound.setDefaultSampleSet(osu.sampleSet); HitSound.setDefaultSampleSet(osu.sampleSet);
MultiClip.destroyExtraClips(); MultiClip.destroyExtraClips();
((Game) game.getState(Opsu.STATE_GAME)).setRestart(Game.Restart.NEW); ((Game) game.getState(Opsu.STATE_GAME)).setRestart(Game.Restart.NEW);
game.enterState(Opsu.STATE_GAME, new FadeOutTransition(Color.black), new FadeInTransition(Color.black)); game.enterState(Opsu.STATE_GAME, new FadeOutTransition(Color.black), new FadeInTransition(Color.black));