Loading apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java +47 −0 Original line number Diff line number Diff line Loading @@ -24,10 +24,12 @@ import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; import android.util.Log; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import java.io.File; import java.util.ArrayList; import java.util.Random; import org.junit.After; import org.junit.Before; Loading Loading @@ -106,6 +108,51 @@ public class SQLiteDatabasePerfTest { } } @Test public void testSelectCacheMissRate() { BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); insertT1TestDataSet(); ArrayList<String> queryPool = new ArrayList<>(); queryPool.add("SELECT _ID, COL_A, COL_B, COL_C FROM T1 WHERE _ID=?"); queryPool.add("SELECT _ID FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_A FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_B FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_C FROM T1 WHERE _ID=?"); queryPool.add("SELECT _ID, COL_A FROM T1 WHERE _ID=?"); queryPool.add("SELECT _ID, COL_B FROM T1 WHERE _ID=?"); queryPool.add("SELECT _ID, COL_C FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_A, COL_B FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_A, COL_C FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_B, COL_C FROM T1 WHERE _ID=?"); while (state.keepRunning()) { Random rnd = new Random(0); int queries = 1000; for (int iQuery = 0; iQuery < queries; ++iQuery) { int queryIndex = rnd.nextInt(queryPool.size()); int index = rnd.nextInt(DEFAULT_DATASET_SIZE); try (Cursor cursor = mDatabase.rawQuery( queryPool.get(queryIndex), new String[] {String.valueOf(index)})) { assertTrue(cursor.moveToNext()); } } } Log.d("testSelectMemory", "cacheMissRate: " + mDatabase.getStatementCacheMissRate() + "Total Statements: " + mDatabase.getTotalPreparedStatements() + ". Misses: " + mDatabase.getTotalStatementCacheMisses()); // Make sure caching is working and our miss rate should definitely be less than 100% // however, we would expect this number to be actually closer to 0. assertTrue(mDatabase.getStatementCacheMissRate() < 1); mDatabase.close(); mContext.deleteDatabase(DB_NAME); } @Test public void testSelectMultipleRows() { insertT1TestDataSet(); Loading core/api/test-current.txt +2 −2 Original line number Diff line number Diff line Loading @@ -975,8 +975,8 @@ package android.database.sqlite { } public static class SQLiteDebug.DbStats { ctor public SQLiteDebug.DbStats(String, long, long, int, int, int, int); field public String cache; ctor public SQLiteDebug.DbStats(@NonNull String, long, long, int, int, int, int, boolean); field public final boolean arePoolStats; field public String dbName; field public long dbSize; field public int lookaside; Loading core/java/android/app/ActivityThread.java +36 −8 Original line number Diff line number Diff line Loading @@ -1026,7 +1026,10 @@ public final class ActivityThread extends ClientTransactionHandler } private class ApplicationThread extends IApplicationThread.Stub { private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s"; private static final String DB_CONNECTION_INFO_HEADER = " %8s %8s %14s %5s %5s %5s %s"; private static final String DB_CONNECTION_INFO_FORMAT = " %8s %8s %14s %5d %5d %5d %s"; private static final String DB_POOL_INFO_HEADER = " %13s %13s %13s %s"; private static final String DB_POOL_INFO_FORMAT = " %13d %13d %13d %s"; public final void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras, Loading Loading @@ -1454,8 +1457,9 @@ public final class ActivityThread extends ClientTransactionHandler pw.print(','); pw.print(dbStats.pageSize); pw.print(','); pw.print(dbStats.dbSize); pw.print(','); pw.print(dbStats.lookaside); pw.print(','); pw.print(dbStats.cache); pw.print(','); pw.print(dbStats.cache); pw.print(','); pw.print(dbStats.cacheHits); pw.print(','); pw.print(dbStats.cacheMisses); pw.print(','); pw.print(dbStats.cacheSize); } pw.println(); Loading Loading @@ -1490,15 +1494,34 @@ public final class ActivityThread extends ClientTransactionHandler int N = stats.dbStats.size(); if (N > 0) { pw.println(" DATABASES"); printRow(pw, DB_INFO_FORMAT, "pgsz", "dbsz", "Lookaside(b)", "cache", "Dbname"); printRow(pw, DB_CONNECTION_INFO_HEADER, "pgsz", "dbsz", "Lookaside(b)", "cache hits", "cache misses", "cache size", "Dbname"); pw.println("PER CONNECTION STATS"); for (int i = 0; i < N; i++) { DbStats dbStats = stats.dbStats.get(i); printRow(pw, DB_INFO_FORMAT, if (dbStats.arePoolStats) { // these will be printed after continue; } printRow(pw, DB_CONNECTION_INFO_FORMAT, (dbStats.pageSize > 0) ? String.valueOf(dbStats.pageSize) : " ", (dbStats.dbSize > 0) ? String.valueOf(dbStats.dbSize) : " ", (dbStats.lookaside > 0) ? String.valueOf(dbStats.lookaside) : " ", dbStats.cache, dbStats.dbName); dbStats.cacheHits, dbStats.cacheMisses, dbStats.cacheSize, dbStats.dbName); } // Print stats accumulated through all the connections that have existed in the // pool since it was opened. pw.println("POOL STATS"); printRow(pw, DB_POOL_INFO_HEADER, "cache hits", "cache misses", "cache size", "Dbname"); for (int i = 0; i < N; i++) { DbStats dbStats = stats.dbStats.get(i); if (!dbStats.arePoolStats) { continue; } printRow(pw, DB_POOL_INFO_FORMAT, dbStats.cacheHits, dbStats.cacheMisses, dbStats.cacheSize, dbStats.dbName); } } Loading Loading @@ -1623,7 +1646,12 @@ public final class ActivityThread extends ClientTransactionHandler proto.write(MemInfoDumpProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize); proto.write(MemInfoDumpProto.AppData.SqlStats.Database.LOOKASIDE_B, dbStats.lookaside); proto.write(MemInfoDumpProto.AppData.SqlStats.Database.CACHE, dbStats.cache); proto.write( MemInfoDumpProto.AppData.SqlStats.Database.CACHE_HITS, dbStats.cacheHits); proto.write(MemInfoDumpProto.AppData.SqlStats.Database.CACHE_MISSES, dbStats.cacheMisses); proto.write( MemInfoDumpProto.AppData.SqlStats.Database.CACHE_SIZE, dbStats.cacheSize); proto.end(dToken); } proto.end(sToken); Loading core/java/android/database/sqlite/SQLiteConnection.java +6 −5 Original line number Diff line number Diff line Loading @@ -1053,6 +1053,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } private PreparedStatement acquirePreparedStatement(String sql) { ++mPool.mTotalPrepareStatements; PreparedStatement statement = mPreparedStatementCache.get(sql); boolean skipCache = false; if (statement != null) { Loading @@ -1064,7 +1065,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen // statement but do not cache it. skipCache = true; } ++mPool.mTotalPrepareStatementCacheMiss; final long statementPtr = nativePrepareStatement(mConnectionPtr, sql); try { final int numParameters = nativeGetParameterCount(mConnectionPtr, statementPtr); Loading Loading @@ -1320,7 +1321,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen if (!path.isEmpty()) { label.append(": ").append(path); } dbStatsList.add(new DbStats(label.toString(), pageCount, pageSize, 0, 0, 0, 0)); dbStatsList.add( new DbStats(label.toString(), pageCount, pageSize, 0, 0, 0, 0, false)); } } catch (SQLiteException ex) { // Ignore. Loading Loading @@ -1349,9 +1351,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen label = mConfiguration.path + " (" + mConnectionId + ")"; } return new DbStats(label, pageCount, pageSize, lookaside, mPreparedStatementCache.hitCount(), mPreparedStatementCache.missCount(), mPreparedStatementCache.size()); mPreparedStatementCache.hitCount(), mPreparedStatementCache.missCount(), mPreparedStatementCache.size(), false); } @Override Loading core/java/android/database/sqlite/SQLiteConnectionPool.java +21 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.util.Printer; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import dalvik.annotation.optimization.NeverCompile; import dalvik.system.CloseGuard; import java.io.Closeable; Loading Loading @@ -103,6 +104,10 @@ public final class SQLiteConnectionPool implements Closeable { new ArrayList<SQLiteConnection>(); private SQLiteConnection mAvailablePrimaryConnection; // Prepare statement cache statistics public int mTotalPrepareStatementCacheMiss = 0; public int mTotalPrepareStatements = 0; @GuardedBy("mLock") private IdleConnectionHandler mIdleConnectionHandler; Loading Loading @@ -507,6 +512,12 @@ public final class SQLiteConnectionPool implements Closeable { for (SQLiteConnection connection : mAcquiredConnections.keySet()) { connection.collectDbStatsUnsafe(dbStatsList); } // Global pool stats DbStats poolStats = new DbStats(mConfiguration.path, 0, 0, 0, mTotalPrepareStatements - mTotalPrepareStatementCacheMiss, mTotalPrepareStatementCacheMiss, mTotalPrepareStatements, true); dbStatsList.add(poolStats); } } Loading Loading @@ -1203,6 +1214,16 @@ public final class SQLiteConnectionPool implements Closeable { } } /** @hide */ @NeverCompile public double getStatementCacheMissRate() { if (mTotalPrepareStatements == 0) { // no statements executed thus no miss rate. return 0; } return (double) mTotalPrepareStatementCacheMiss / (double) mTotalPrepareStatements; } public long getTotalStatementsTime() { return mTotalStatementsTime.get(); } Loading Loading
apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java +47 −0 Original line number Diff line number Diff line Loading @@ -24,10 +24,12 @@ import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.perftests.utils.BenchmarkState; import android.perftests.utils.PerfStatusReporter; import android.util.Log; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import java.io.File; import java.util.ArrayList; import java.util.Random; import org.junit.After; import org.junit.Before; Loading Loading @@ -106,6 +108,51 @@ public class SQLiteDatabasePerfTest { } } @Test public void testSelectCacheMissRate() { BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); insertT1TestDataSet(); ArrayList<String> queryPool = new ArrayList<>(); queryPool.add("SELECT _ID, COL_A, COL_B, COL_C FROM T1 WHERE _ID=?"); queryPool.add("SELECT _ID FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_A FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_B FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_C FROM T1 WHERE _ID=?"); queryPool.add("SELECT _ID, COL_A FROM T1 WHERE _ID=?"); queryPool.add("SELECT _ID, COL_B FROM T1 WHERE _ID=?"); queryPool.add("SELECT _ID, COL_C FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_A, COL_B FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_A, COL_C FROM T1 WHERE _ID=?"); queryPool.add("SELECT COL_B, COL_C FROM T1 WHERE _ID=?"); while (state.keepRunning()) { Random rnd = new Random(0); int queries = 1000; for (int iQuery = 0; iQuery < queries; ++iQuery) { int queryIndex = rnd.nextInt(queryPool.size()); int index = rnd.nextInt(DEFAULT_DATASET_SIZE); try (Cursor cursor = mDatabase.rawQuery( queryPool.get(queryIndex), new String[] {String.valueOf(index)})) { assertTrue(cursor.moveToNext()); } } } Log.d("testSelectMemory", "cacheMissRate: " + mDatabase.getStatementCacheMissRate() + "Total Statements: " + mDatabase.getTotalPreparedStatements() + ". Misses: " + mDatabase.getTotalStatementCacheMisses()); // Make sure caching is working and our miss rate should definitely be less than 100% // however, we would expect this number to be actually closer to 0. assertTrue(mDatabase.getStatementCacheMissRate() < 1); mDatabase.close(); mContext.deleteDatabase(DB_NAME); } @Test public void testSelectMultipleRows() { insertT1TestDataSet(); Loading
core/api/test-current.txt +2 −2 Original line number Diff line number Diff line Loading @@ -975,8 +975,8 @@ package android.database.sqlite { } public static class SQLiteDebug.DbStats { ctor public SQLiteDebug.DbStats(String, long, long, int, int, int, int); field public String cache; ctor public SQLiteDebug.DbStats(@NonNull String, long, long, int, int, int, int, boolean); field public final boolean arePoolStats; field public String dbName; field public long dbSize; field public int lookaside; Loading
core/java/android/app/ActivityThread.java +36 −8 Original line number Diff line number Diff line Loading @@ -1026,7 +1026,10 @@ public final class ActivityThread extends ClientTransactionHandler } private class ApplicationThread extends IApplicationThread.Stub { private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s"; private static final String DB_CONNECTION_INFO_HEADER = " %8s %8s %14s %5s %5s %5s %s"; private static final String DB_CONNECTION_INFO_FORMAT = " %8s %8s %14s %5d %5d %5d %s"; private static final String DB_POOL_INFO_HEADER = " %13s %13s %13s %s"; private static final String DB_POOL_INFO_FORMAT = " %13d %13d %13d %s"; public final void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras, Loading Loading @@ -1454,8 +1457,9 @@ public final class ActivityThread extends ClientTransactionHandler pw.print(','); pw.print(dbStats.pageSize); pw.print(','); pw.print(dbStats.dbSize); pw.print(','); pw.print(dbStats.lookaside); pw.print(','); pw.print(dbStats.cache); pw.print(','); pw.print(dbStats.cache); pw.print(','); pw.print(dbStats.cacheHits); pw.print(','); pw.print(dbStats.cacheMisses); pw.print(','); pw.print(dbStats.cacheSize); } pw.println(); Loading Loading @@ -1490,15 +1494,34 @@ public final class ActivityThread extends ClientTransactionHandler int N = stats.dbStats.size(); if (N > 0) { pw.println(" DATABASES"); printRow(pw, DB_INFO_FORMAT, "pgsz", "dbsz", "Lookaside(b)", "cache", "Dbname"); printRow(pw, DB_CONNECTION_INFO_HEADER, "pgsz", "dbsz", "Lookaside(b)", "cache hits", "cache misses", "cache size", "Dbname"); pw.println("PER CONNECTION STATS"); for (int i = 0; i < N; i++) { DbStats dbStats = stats.dbStats.get(i); printRow(pw, DB_INFO_FORMAT, if (dbStats.arePoolStats) { // these will be printed after continue; } printRow(pw, DB_CONNECTION_INFO_FORMAT, (dbStats.pageSize > 0) ? String.valueOf(dbStats.pageSize) : " ", (dbStats.dbSize > 0) ? String.valueOf(dbStats.dbSize) : " ", (dbStats.lookaside > 0) ? String.valueOf(dbStats.lookaside) : " ", dbStats.cache, dbStats.dbName); dbStats.cacheHits, dbStats.cacheMisses, dbStats.cacheSize, dbStats.dbName); } // Print stats accumulated through all the connections that have existed in the // pool since it was opened. pw.println("POOL STATS"); printRow(pw, DB_POOL_INFO_HEADER, "cache hits", "cache misses", "cache size", "Dbname"); for (int i = 0; i < N; i++) { DbStats dbStats = stats.dbStats.get(i); if (!dbStats.arePoolStats) { continue; } printRow(pw, DB_POOL_INFO_FORMAT, dbStats.cacheHits, dbStats.cacheMisses, dbStats.cacheSize, dbStats.dbName); } } Loading Loading @@ -1623,7 +1646,12 @@ public final class ActivityThread extends ClientTransactionHandler proto.write(MemInfoDumpProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize); proto.write(MemInfoDumpProto.AppData.SqlStats.Database.LOOKASIDE_B, dbStats.lookaside); proto.write(MemInfoDumpProto.AppData.SqlStats.Database.CACHE, dbStats.cache); proto.write( MemInfoDumpProto.AppData.SqlStats.Database.CACHE_HITS, dbStats.cacheHits); proto.write(MemInfoDumpProto.AppData.SqlStats.Database.CACHE_MISSES, dbStats.cacheMisses); proto.write( MemInfoDumpProto.AppData.SqlStats.Database.CACHE_SIZE, dbStats.cacheSize); proto.end(dToken); } proto.end(sToken); Loading
core/java/android/database/sqlite/SQLiteConnection.java +6 −5 Original line number Diff line number Diff line Loading @@ -1053,6 +1053,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } private PreparedStatement acquirePreparedStatement(String sql) { ++mPool.mTotalPrepareStatements; PreparedStatement statement = mPreparedStatementCache.get(sql); boolean skipCache = false; if (statement != null) { Loading @@ -1064,7 +1065,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen // statement but do not cache it. skipCache = true; } ++mPool.mTotalPrepareStatementCacheMiss; final long statementPtr = nativePrepareStatement(mConnectionPtr, sql); try { final int numParameters = nativeGetParameterCount(mConnectionPtr, statementPtr); Loading Loading @@ -1320,7 +1321,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen if (!path.isEmpty()) { label.append(": ").append(path); } dbStatsList.add(new DbStats(label.toString(), pageCount, pageSize, 0, 0, 0, 0)); dbStatsList.add( new DbStats(label.toString(), pageCount, pageSize, 0, 0, 0, 0, false)); } } catch (SQLiteException ex) { // Ignore. Loading Loading @@ -1349,9 +1351,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen label = mConfiguration.path + " (" + mConnectionId + ")"; } return new DbStats(label, pageCount, pageSize, lookaside, mPreparedStatementCache.hitCount(), mPreparedStatementCache.missCount(), mPreparedStatementCache.size()); mPreparedStatementCache.hitCount(), mPreparedStatementCache.missCount(), mPreparedStatementCache.size(), false); } @Override Loading
core/java/android/database/sqlite/SQLiteConnectionPool.java +21 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.util.Printer; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import dalvik.annotation.optimization.NeverCompile; import dalvik.system.CloseGuard; import java.io.Closeable; Loading Loading @@ -103,6 +104,10 @@ public final class SQLiteConnectionPool implements Closeable { new ArrayList<SQLiteConnection>(); private SQLiteConnection mAvailablePrimaryConnection; // Prepare statement cache statistics public int mTotalPrepareStatementCacheMiss = 0; public int mTotalPrepareStatements = 0; @GuardedBy("mLock") private IdleConnectionHandler mIdleConnectionHandler; Loading Loading @@ -507,6 +512,12 @@ public final class SQLiteConnectionPool implements Closeable { for (SQLiteConnection connection : mAcquiredConnections.keySet()) { connection.collectDbStatsUnsafe(dbStatsList); } // Global pool stats DbStats poolStats = new DbStats(mConfiguration.path, 0, 0, 0, mTotalPrepareStatements - mTotalPrepareStatementCacheMiss, mTotalPrepareStatementCacheMiss, mTotalPrepareStatements, true); dbStatsList.add(poolStats); } } Loading Loading @@ -1203,6 +1214,16 @@ public final class SQLiteConnectionPool implements Closeable { } } /** @hide */ @NeverCompile public double getStatementCacheMissRate() { if (mTotalPrepareStatements == 0) { // no statements executed thus no miss rate. return 0; } return (double) mTotalPrepareStatementCacheMiss / (double) mTotalPrepareStatements; } public long getTotalStatementsTime() { return mTotalStatementsTime.get(); } Loading