Loading tests/TileBenchmark/res/values/strings.xml +12 −4 Original line number Diff line number Diff line Loading @@ -49,8 +49,9 @@ <!-- Drop down menu entry - automatically scroll to the end of the page with scrollBy() [CHAR LIMIT=15] --> <string name="movement_auto_scroll">Auto-scroll</string> <!-- Drop down menu entry - [CHAR LIMIT=15] --> <string name="movement_auto_fling">Auto-fling</string> <!-- Drop down menu entry - automatically record for a set time before stopping [CHAR LIMIT=15] --> <string name="movement_timed">Timed</string> <!-- Drop down menu entry - manually navigate the page(s), hit 'capture' button [CHAR LIMIT=15] --> <string name="movement_manual">Manual</string> Loading @@ -67,14 +68,21 @@ <!-- 75th percentile - 75% of frames fall below this value [CHAR LIMIT=12] --> <string name="percentile_75">75%ile</string> <!-- standard deviation [CHAR LIMIT=12] --> <string name="std_dev">StdDev</string> <!-- mean [CHAR LIMIT=12] --> <string name="mean">mean</string> <!-- Frame rate [CHAR LIMIT=15] --> <string name="frames_per_second">Frames/sec</string> <!-- Portion of viewport covered by good tiles [CHAR LIMIT=15] --> <string name="viewport_coverage">Coverage</string> <!-- Milliseconds taken to inval, and re-render the page [CHAR LIMIT=15] --> <string name="render_millis">RenderMillis</string> <!-- Number of rendering stalls while running the test [CHAR LIMIT=15] --> <string name="render_stalls">Stalls</string> <!-- Animation Framerate [CHAR LIMIT=15] --> <string name="animation_framerate">AnimFramerate</string> <!-- Format string for stat value overlay [CHAR LIMIT=15] --> <string name="format_stat">%4.4f</string> Loading tests/TileBenchmark/src/com/test/tilebenchmark/PerformanceTest.java +171 −34 Original line number Diff line number Diff line Loading @@ -27,17 +27,57 @@ import android.os.Bundle; import android.os.Environment; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; import android.webkit.WebSettings; import android.widget.Spinner; public class PerformanceTest extends ActivityInstrumentationTestCase2<ProfileActivity> { public static class AnimStat { double aggVal = 0; double aggSqrVal = 0; double count = 0; } private class StatAggregator extends PlaybackGraphs { private HashMap<String, Double> mDataMap = new HashMap<String, Double>(); private HashMap<String, AnimStat> mAnimDataMap = new HashMap<String, AnimStat>(); private int mCount = 0; public void aggregate() { boolean inAnimTests = mAnimTests != null; Resources resources = mWeb.getResources(); String animFramerateString = resources.getString(R.string.animation_framerate); for (Map.Entry<String, Double> e : mSingleStats.entrySet()) { String name = e.getKey(); if (inAnimTests) { if (name.equals(animFramerateString)) { // in animation testing phase, record animation framerate and aggregate // stats, differentiating on values of mAnimTestNr and mDoubleBuffering String fullName = ANIM_TEST_NAMES[mAnimTestNr] + " " + name; fullName += mDoubleBuffering ? " tiled" : " webkit"; if (!mAnimDataMap.containsKey(fullName)) { mAnimDataMap.put(fullName, new AnimStat()); } AnimStat statVals = mAnimDataMap.get(fullName); statVals.aggVal += e.getValue(); statVals.aggSqrVal += e.getValue() * e.getValue(); statVals.count += 1; } } else { double aggVal = mDataMap.containsKey(name) ? mDataMap.get(name) : 0; mDataMap.put(name, aggVal + e.getValue()); } } if (inAnimTests) { return; } mCount++; Resources resources = mView.getResources(); for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) { for (int statIndex = 0; statIndex < Stats.length; statIndex++) { String metricLabel = resources.getString( Loading @@ -53,34 +93,47 @@ public class PerformanceTest extends mDataMap.put(label, aggVal); } } for (Map.Entry<String, Double> e : mSingleStats.entrySet()) { double aggVal = mDataMap.containsKey(e.getKey()) ? mDataMap.get(e.getKey()) : 0; mDataMap.put(e.getKey(), aggVal + e.getValue()); } } // build the final bundle of results public Bundle getBundle() { Bundle b = new Bundle(); int count = 0 == mCount ? Integer.MAX_VALUE : mCount; int count = (0 == mCount) ? Integer.MAX_VALUE : mCount; for (Map.Entry<String, Double> e : mDataMap.entrySet()) { b.putDouble(e.getKey(), e.getValue() / count); } for (Map.Entry<String, AnimStat> e : mAnimDataMap.entrySet()) { String statName = e.getKey(); AnimStat statVals = e.getValue(); double avg = statVals.aggVal/statVals.count; double stdDev = Math.sqrt((statVals.aggSqrVal / statVals.count) - avg * avg); b.putDouble(statName, avg); b.putDouble(statName + " STD DEV", stdDev); } return b; } } ProfileActivity mActivity; ProfiledWebView mView; StatAggregator mStats = new StatAggregator(); ProfiledWebView mWeb; Spinner mMovementSpinner; StatAggregator mStats; private static final String LOGTAG = "PerformanceTest"; private static final String TEST_LOCATION = "webkit/page_cycler"; private static final String URL_PREFIX = "file://"; private static final String URL_POSTFIX = "/index.html?skip=true"; private static final int MAX_ITERATIONS = 4; private static final String TEST_DIRS[] = { "alexa25_2011"//, "alexa_us", "android", "dom", "intl2", "moz", "moz2" private static final String SCROLL_TEST_DIRS[] = { "alexa25_2011" }; private static final String ANIM_TEST_DIRS[] = { "dhtml" }; public PerformanceTest() { Loading @@ -91,7 +144,22 @@ public class PerformanceTest extends protected void setUp() throws Exception { super.setUp(); mActivity = getActivity(); mView = (ProfiledWebView) mActivity.findViewById(R.id.web); mWeb = (ProfiledWebView) mActivity.findViewById(R.id.web); mMovementSpinner = (Spinner) mActivity.findViewById(R.id.movement); mStats = new StatAggregator(); // use mStats as a condition variable between the UI thread and // this(the testing) thread mActivity.setCallback(new ProfileCallback() { @Override public void profileCallback(RunData data) { mStats.setData(data); synchronized (mStats) { mStats.notify(); } } }); } private boolean loadUrl(final String url) { Loading @@ -100,12 +168,13 @@ public class PerformanceTest extends mActivity.runOnUiThread(new Runnable() { @Override public void run() { mView.loadUrl(url); mWeb.loadUrl(url); } }); synchronized (mStats) { mStats.wait(); } mStats.aggregate(); } catch (InterruptedException e) { e.printStackTrace(); Loading @@ -114,15 +183,30 @@ public class PerformanceTest extends return true; } private boolean runIteration() { private boolean validTest(String nextTest) { // if testing animations, test must be in mAnimTests if (mAnimTests == null) return true; for (String test : mAnimTests) { if (test.equals(nextTest)) { return true; } } return false; } private boolean runIteration(String[] testDirs) { File sdFile = Environment.getExternalStorageDirectory(); for (String testDirName : TEST_DIRS) { for (String testDirName : testDirs) { File testDir = new File(sdFile, TEST_LOCATION + "/" + testDirName); Log.d(LOGTAG, "Testing dir: '" + testDir.getAbsolutePath() + "', exists=" + testDir.exists()); for (File siteDir : testDir.listFiles()) { if (!siteDir.isDirectory()) if (!siteDir.isDirectory() || !validTest(siteDir.getName())) { continue; } if (!loadUrl(URL_PREFIX + siteDir.getAbsolutePath() + URL_POSTFIX)) { Loading @@ -133,7 +217,44 @@ public class PerformanceTest extends return true; } public void testMetrics() { private boolean runTestDirs(String[] testDirs) { for (int i = 0; i < MAX_ITERATIONS; i++) if (!runIteration(testDirs)) { return false; } return true; } private void pushDoubleBuffering() { getInstrumentation().runOnMainSync(new Runnable() { public void run() { mWeb.setDoubleBuffering(mDoubleBuffering); } }); } private void setScrollingTestingMode(final boolean scrolled) { getInstrumentation().runOnMainSync(new Runnable() { public void run() { mMovementSpinner.setSelection(scrolled ? 0 : 2); } }); } private String[] mAnimTests = null; private int mAnimTestNr = -1; private boolean mDoubleBuffering = true; private static final String[] ANIM_TEST_NAMES = { "slow", "fast" }; private static final String[][] ANIM_TESTS = { {"scrolling", "replaceimages", "layers5", "layers1"}, {"slidingballs", "meter", "slidein", "fadespacing", "colorfade", "mozilla", "movingtext", "diagball", "zoom", "imageslide"}, }; private boolean checkMedia() { String state = Environment.getExternalStorageState(); if (!Environment.MEDIA_MOUNTED.equals(state) Loading @@ -141,27 +262,43 @@ public class PerformanceTest extends Log.d(LOGTAG, "ARG Can't access sd card!"); // Can't read the SD card, fail and die! getInstrumentation().sendStatus(1, null); return; return false; } return true; } // use mGraphs as a condition variable between the UI thread and // this(the testing) thread mActivity.setCallback(new ProfileCallback() { @Override public void profileCallback(RunData data) { Log.d(LOGTAG, "test completion callback"); mStats.setData(data); synchronized (mStats) { mStats.notify(); public void testMetrics() { setScrollingTestingMode(true); if (checkMedia() && runTestDirs(SCROLL_TEST_DIRS)) { getInstrumentation().sendStatus(0, mStats.getBundle()); } else { getInstrumentation().sendStatus(1, null); } } }); for (int i = 0; i < MAX_ITERATIONS; i++) if (!runIteration()) { getInstrumentation().sendStatus(1, null); return; private boolean runAnimationTests() { for (int doubleBuffer = 0; doubleBuffer <= 1; doubleBuffer++) { mDoubleBuffering = doubleBuffer == 1; pushDoubleBuffering(); for (mAnimTestNr = 0; mAnimTestNr < ANIM_TESTS.length; mAnimTestNr++) { mAnimTests = ANIM_TESTS[mAnimTestNr]; if (!runTestDirs(ANIM_TEST_DIRS)) { return false; } } } return true; } public void testAnimations() { // instead of autoscrolling, load each page until either an timer fires, // or the animation signals complete via javascript setScrollingTestingMode(false); if (checkMedia() && runAnimationTests()) { getInstrumentation().sendStatus(0, mStats.getBundle()); } else { getInstrumentation().sendStatus(1, null); } } } tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java +80 −27 Original line number Diff line number Diff line Loading @@ -80,7 +80,7 @@ public class PlaybackGraphs { for (int tileID = 1; tileID < frame.length; tileID++) { TileData data = frame[tileID]; double coverage = viewportCoverage(frame[0], data); total += coverage * (data.isReady ? 1 : 0); total += coverage * (data.isReady ? 100 : 0); totalCount += coverage; } if (totalCount == 0) { Loading @@ -91,7 +91,7 @@ public class PlaybackGraphs { @Override public double getMax() { return 1; return 100; } @Override Loading @@ -108,6 +108,9 @@ public class PlaybackGraphs { } public static double getPercentile(double sortedValues[], double ratioAbove) { if (sortedValues.length == 0) return -1; double index = ratioAbove * (sortedValues.length - 1); int intIndex = (int) Math.floor(index); if (index == intIndex) { Loading @@ -118,6 +121,31 @@ public class PlaybackGraphs { + sortedValues[intIndex + 1] * (alpha); } public static double getMean(double sortedValues[]) { if (sortedValues.length == 0) return -1; double agg = 0; for (double val : sortedValues) { agg += val; } return agg / sortedValues.length; } public static double getStdDev(double sortedValues[]) { if (sortedValues.length == 0) return -1; double agg = 0; double sqrAgg = 0; for (double val : sortedValues) { agg += val; sqrAgg += val*val; } double mean = agg / sortedValues.length; return Math.sqrt((sqrAgg / sortedValues.length) - (mean * mean)); } protected static StatGen[] Stats = new StatGen[] { new StatGen() { @Override Loading Loading @@ -149,6 +177,26 @@ public class PlaybackGraphs { public int getLabelId() { return R.string.percentile_75; } }, new StatGen() { @Override public double getValue(double[] sortedValues) { return getStdDev(sortedValues); } @Override public int getLabelId() { return R.string.std_dev; } }, new StatGen() { @Override public double getValue(double[] sortedValues) { return getMean(sortedValues); } @Override public int getLabelId() { return R.string.mean; } }, }; Loading @@ -159,18 +207,10 @@ public class PlaybackGraphs { } private ArrayList<ShapeDrawable> mShapes = new ArrayList<ShapeDrawable>(); protected double[][] mStats = new double[Metrics.length][Stats.length]; protected final double[][] mStats = new double[Metrics.length][Stats.length]; protected HashMap<String, Double> mSingleStats; public void setData(RunData data) { mShapes.clear(); double metricValues[] = new double[data.frames.length]; if (data.frames.length == 0) { return; } for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) { private void gatherFrameMetric(int metricIndex, double metricValues[], RunData data) { // create graph out of rectangles, one per frame int lastBar = 0; for (int frameIndex = 0; frameIndex < data.frames.length; frameIndex++) { Loading @@ -193,6 +233,21 @@ public class PlaybackGraphs { metricValues[frameIndex] = absoluteValue; lastBar = newBar; } } public void setData(RunData data) { mShapes.clear(); double metricValues[] = new double[data.frames.length]; mSingleStats = data.singleStats; if (data.frames.length == 0) { return; } for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) { // calculate metric based on list of frames gatherFrameMetric(metricIndex, metricValues, data); // store aggregate statistics per metric (median, and similar) Arrays.sort(metricValues); Loading @@ -200,8 +255,6 @@ public class PlaybackGraphs { mStats[metricIndex][statIndex] = Stats[statIndex].getValue(metricValues); } mSingleStats = data.singleStats; } } Loading tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java +49 −20 Original line number Diff line number Diff line Loading @@ -22,11 +22,12 @@ import android.content.Context; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; import android.os.CountDownTimer; import android.util.Log; import android.util.Pair; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.AdapterView; Loading @@ -49,6 +50,8 @@ import java.io.ObjectOutputStream; */ public class ProfileActivity extends Activity { private static final int TIMED_RECORD_MILLIS = 2000; public interface ProfileCallback { public void profileCallback(RunData data); } Loading @@ -65,6 +68,7 @@ public class ProfileActivity extends Activity { LoggingWebViewClient mLoggingWebViewClient = new LoggingWebViewClient(); AutoLoggingWebViewClient mAutoLoggingWebViewClient = new AutoLoggingWebViewClient(); TimedLoggingWebViewClient mTimedLoggingWebViewClient = new TimedLoggingWebViewClient(); private enum TestingState { NOT_TESTING, Loading Loading @@ -93,18 +97,18 @@ public class ProfileActivity extends Activity { public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { String movementStr = parent.getItemAtPosition(position).toString(); if (movementStr == getResources().getString( R.string.movement_auto_scroll) || movementStr == getResources().getString( R.string.movement_auto_fling)) { if (movementStr == getResources().getString(R.string.movement_auto_scroll)) { mWeb.setWebViewClient(mAutoLoggingWebViewClient); mCaptureButton.setEnabled(false); mVelocitySpinner.setEnabled(true); } else if (movementStr == getResources().getString( R.string.movement_manual)) { } else if (movementStr == getResources().getString(R.string.movement_manual)) { mWeb.setWebViewClient(mLoggingWebViewClient); mCaptureButton.setEnabled(true); mVelocitySpinner.setEnabled(false); } else if (movementStr == getResources().getString(R.string.movement_timed)) { mWeb.setWebViewClient(mTimedLoggingWebViewClient); mCaptureButton.setEnabled(false); mVelocitySpinner.setEnabled(false); } } Loading @@ -124,16 +128,46 @@ public class ProfileActivity extends Activity { super.onPageStarted(view, url, favicon); mUrl.setText(url); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); view.requestFocus(); ((ProfiledWebView)view).onPageFinished(); } } private class AutoLoggingWebViewClient extends LoggingWebViewClient { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); startViewProfiling(true); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); setTestingState(TestingState.PRE_TESTING); } } private class TimedLoggingWebViewClient extends LoggingWebViewClient { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); view.requestFocus(); startViewProfiling(false); startViewProfiling(true); // after a fixed time after page finished, stop testing new CountDownTimer(TIMED_RECORD_MILLIS, TIMED_RECORD_MILLIS) { @Override public void onTick(long millisUntilFinished) { } @Override public void onFinish() { mWeb.stopScrollTest(); } }.start(); } @Override Loading Loading @@ -178,11 +212,13 @@ public class ProfileActivity extends Activity { mMovementSpinner.setEnabled(false); break; case START_TESTING: mCaptureButton.setChecked(true); mUrl.setBackgroundResource(R.color.background_start_testing); mInspectButton.setEnabled(false); mMovementSpinner.setEnabled(false); break; case STOP_TESTING: mCaptureButton.setChecked(false); mUrl.setBackgroundResource(R.color.background_stop_testing); break; case SAVED_TESTING: Loading @@ -195,7 +231,6 @@ public class ProfileActivity extends Activity { /** auto - automatically scroll. */ private void startViewProfiling(boolean auto) { // toggle capture button to indicate capture state to user mCaptureButton.setChecked(true); mWeb.startScrollTest(mCallback, auto); setTestingState(TestingState.START_TESTING); } Loading @@ -217,7 +252,7 @@ public class ProfileActivity extends Activity { public void profileCallback(RunData data) { new StoreFileTask().execute(new Pair<String, RunData>( TEMP_FILENAME, data)); mCaptureButton.setChecked(false); Log.d("ProfileActivity", "stored " + data.frames.length + " frames in file"); setTestingState(TestingState.STOP_TESTING); } }); Loading Loading @@ -245,8 +280,8 @@ public class ProfileActivity extends Activity { // Movement spinner String content[] = { getResources().getString(R.string.movement_auto_scroll), getResources().getString(R.string.movement_auto_fling), getResources().getString(R.string.movement_manual) getResources().getString(R.string.movement_manual), getResources().getString(R.string.movement_timed) }; adapter = new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_item, content); Loading @@ -270,13 +305,7 @@ public class ProfileActivity extends Activity { }); // Custom profiling WebView WebSettings settings = mWeb.getSettings(); settings.setJavaScriptEnabled(true); settings.setSupportZoom(true); settings.setEnableSmoothTransition(true); settings.setBuiltInZoomControls(true); settings.setLoadWithOverviewMode(true); settings.setProperty("use_minimal_memory", "false"); // prefetch tiles, as browser does mWeb.init(this); mWeb.setWebViewClient(new LoggingWebViewClient()); // URL text entry Loading tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java +88 −15 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
tests/TileBenchmark/res/values/strings.xml +12 −4 Original line number Diff line number Diff line Loading @@ -49,8 +49,9 @@ <!-- Drop down menu entry - automatically scroll to the end of the page with scrollBy() [CHAR LIMIT=15] --> <string name="movement_auto_scroll">Auto-scroll</string> <!-- Drop down menu entry - [CHAR LIMIT=15] --> <string name="movement_auto_fling">Auto-fling</string> <!-- Drop down menu entry - automatically record for a set time before stopping [CHAR LIMIT=15] --> <string name="movement_timed">Timed</string> <!-- Drop down menu entry - manually navigate the page(s), hit 'capture' button [CHAR LIMIT=15] --> <string name="movement_manual">Manual</string> Loading @@ -67,14 +68,21 @@ <!-- 75th percentile - 75% of frames fall below this value [CHAR LIMIT=12] --> <string name="percentile_75">75%ile</string> <!-- standard deviation [CHAR LIMIT=12] --> <string name="std_dev">StdDev</string> <!-- mean [CHAR LIMIT=12] --> <string name="mean">mean</string> <!-- Frame rate [CHAR LIMIT=15] --> <string name="frames_per_second">Frames/sec</string> <!-- Portion of viewport covered by good tiles [CHAR LIMIT=15] --> <string name="viewport_coverage">Coverage</string> <!-- Milliseconds taken to inval, and re-render the page [CHAR LIMIT=15] --> <string name="render_millis">RenderMillis</string> <!-- Number of rendering stalls while running the test [CHAR LIMIT=15] --> <string name="render_stalls">Stalls</string> <!-- Animation Framerate [CHAR LIMIT=15] --> <string name="animation_framerate">AnimFramerate</string> <!-- Format string for stat value overlay [CHAR LIMIT=15] --> <string name="format_stat">%4.4f</string> Loading
tests/TileBenchmark/src/com/test/tilebenchmark/PerformanceTest.java +171 −34 Original line number Diff line number Diff line Loading @@ -27,17 +27,57 @@ import android.os.Bundle; import android.os.Environment; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; import android.webkit.WebSettings; import android.widget.Spinner; public class PerformanceTest extends ActivityInstrumentationTestCase2<ProfileActivity> { public static class AnimStat { double aggVal = 0; double aggSqrVal = 0; double count = 0; } private class StatAggregator extends PlaybackGraphs { private HashMap<String, Double> mDataMap = new HashMap<String, Double>(); private HashMap<String, AnimStat> mAnimDataMap = new HashMap<String, AnimStat>(); private int mCount = 0; public void aggregate() { boolean inAnimTests = mAnimTests != null; Resources resources = mWeb.getResources(); String animFramerateString = resources.getString(R.string.animation_framerate); for (Map.Entry<String, Double> e : mSingleStats.entrySet()) { String name = e.getKey(); if (inAnimTests) { if (name.equals(animFramerateString)) { // in animation testing phase, record animation framerate and aggregate // stats, differentiating on values of mAnimTestNr and mDoubleBuffering String fullName = ANIM_TEST_NAMES[mAnimTestNr] + " " + name; fullName += mDoubleBuffering ? " tiled" : " webkit"; if (!mAnimDataMap.containsKey(fullName)) { mAnimDataMap.put(fullName, new AnimStat()); } AnimStat statVals = mAnimDataMap.get(fullName); statVals.aggVal += e.getValue(); statVals.aggSqrVal += e.getValue() * e.getValue(); statVals.count += 1; } } else { double aggVal = mDataMap.containsKey(name) ? mDataMap.get(name) : 0; mDataMap.put(name, aggVal + e.getValue()); } } if (inAnimTests) { return; } mCount++; Resources resources = mView.getResources(); for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) { for (int statIndex = 0; statIndex < Stats.length; statIndex++) { String metricLabel = resources.getString( Loading @@ -53,34 +93,47 @@ public class PerformanceTest extends mDataMap.put(label, aggVal); } } for (Map.Entry<String, Double> e : mSingleStats.entrySet()) { double aggVal = mDataMap.containsKey(e.getKey()) ? mDataMap.get(e.getKey()) : 0; mDataMap.put(e.getKey(), aggVal + e.getValue()); } } // build the final bundle of results public Bundle getBundle() { Bundle b = new Bundle(); int count = 0 == mCount ? Integer.MAX_VALUE : mCount; int count = (0 == mCount) ? Integer.MAX_VALUE : mCount; for (Map.Entry<String, Double> e : mDataMap.entrySet()) { b.putDouble(e.getKey(), e.getValue() / count); } for (Map.Entry<String, AnimStat> e : mAnimDataMap.entrySet()) { String statName = e.getKey(); AnimStat statVals = e.getValue(); double avg = statVals.aggVal/statVals.count; double stdDev = Math.sqrt((statVals.aggSqrVal / statVals.count) - avg * avg); b.putDouble(statName, avg); b.putDouble(statName + " STD DEV", stdDev); } return b; } } ProfileActivity mActivity; ProfiledWebView mView; StatAggregator mStats = new StatAggregator(); ProfiledWebView mWeb; Spinner mMovementSpinner; StatAggregator mStats; private static final String LOGTAG = "PerformanceTest"; private static final String TEST_LOCATION = "webkit/page_cycler"; private static final String URL_PREFIX = "file://"; private static final String URL_POSTFIX = "/index.html?skip=true"; private static final int MAX_ITERATIONS = 4; private static final String TEST_DIRS[] = { "alexa25_2011"//, "alexa_us", "android", "dom", "intl2", "moz", "moz2" private static final String SCROLL_TEST_DIRS[] = { "alexa25_2011" }; private static final String ANIM_TEST_DIRS[] = { "dhtml" }; public PerformanceTest() { Loading @@ -91,7 +144,22 @@ public class PerformanceTest extends protected void setUp() throws Exception { super.setUp(); mActivity = getActivity(); mView = (ProfiledWebView) mActivity.findViewById(R.id.web); mWeb = (ProfiledWebView) mActivity.findViewById(R.id.web); mMovementSpinner = (Spinner) mActivity.findViewById(R.id.movement); mStats = new StatAggregator(); // use mStats as a condition variable between the UI thread and // this(the testing) thread mActivity.setCallback(new ProfileCallback() { @Override public void profileCallback(RunData data) { mStats.setData(data); synchronized (mStats) { mStats.notify(); } } }); } private boolean loadUrl(final String url) { Loading @@ -100,12 +168,13 @@ public class PerformanceTest extends mActivity.runOnUiThread(new Runnable() { @Override public void run() { mView.loadUrl(url); mWeb.loadUrl(url); } }); synchronized (mStats) { mStats.wait(); } mStats.aggregate(); } catch (InterruptedException e) { e.printStackTrace(); Loading @@ -114,15 +183,30 @@ public class PerformanceTest extends return true; } private boolean runIteration() { private boolean validTest(String nextTest) { // if testing animations, test must be in mAnimTests if (mAnimTests == null) return true; for (String test : mAnimTests) { if (test.equals(nextTest)) { return true; } } return false; } private boolean runIteration(String[] testDirs) { File sdFile = Environment.getExternalStorageDirectory(); for (String testDirName : TEST_DIRS) { for (String testDirName : testDirs) { File testDir = new File(sdFile, TEST_LOCATION + "/" + testDirName); Log.d(LOGTAG, "Testing dir: '" + testDir.getAbsolutePath() + "', exists=" + testDir.exists()); for (File siteDir : testDir.listFiles()) { if (!siteDir.isDirectory()) if (!siteDir.isDirectory() || !validTest(siteDir.getName())) { continue; } if (!loadUrl(URL_PREFIX + siteDir.getAbsolutePath() + URL_POSTFIX)) { Loading @@ -133,7 +217,44 @@ public class PerformanceTest extends return true; } public void testMetrics() { private boolean runTestDirs(String[] testDirs) { for (int i = 0; i < MAX_ITERATIONS; i++) if (!runIteration(testDirs)) { return false; } return true; } private void pushDoubleBuffering() { getInstrumentation().runOnMainSync(new Runnable() { public void run() { mWeb.setDoubleBuffering(mDoubleBuffering); } }); } private void setScrollingTestingMode(final boolean scrolled) { getInstrumentation().runOnMainSync(new Runnable() { public void run() { mMovementSpinner.setSelection(scrolled ? 0 : 2); } }); } private String[] mAnimTests = null; private int mAnimTestNr = -1; private boolean mDoubleBuffering = true; private static final String[] ANIM_TEST_NAMES = { "slow", "fast" }; private static final String[][] ANIM_TESTS = { {"scrolling", "replaceimages", "layers5", "layers1"}, {"slidingballs", "meter", "slidein", "fadespacing", "colorfade", "mozilla", "movingtext", "diagball", "zoom", "imageslide"}, }; private boolean checkMedia() { String state = Environment.getExternalStorageState(); if (!Environment.MEDIA_MOUNTED.equals(state) Loading @@ -141,27 +262,43 @@ public class PerformanceTest extends Log.d(LOGTAG, "ARG Can't access sd card!"); // Can't read the SD card, fail and die! getInstrumentation().sendStatus(1, null); return; return false; } return true; } // use mGraphs as a condition variable between the UI thread and // this(the testing) thread mActivity.setCallback(new ProfileCallback() { @Override public void profileCallback(RunData data) { Log.d(LOGTAG, "test completion callback"); mStats.setData(data); synchronized (mStats) { mStats.notify(); public void testMetrics() { setScrollingTestingMode(true); if (checkMedia() && runTestDirs(SCROLL_TEST_DIRS)) { getInstrumentation().sendStatus(0, mStats.getBundle()); } else { getInstrumentation().sendStatus(1, null); } } }); for (int i = 0; i < MAX_ITERATIONS; i++) if (!runIteration()) { getInstrumentation().sendStatus(1, null); return; private boolean runAnimationTests() { for (int doubleBuffer = 0; doubleBuffer <= 1; doubleBuffer++) { mDoubleBuffering = doubleBuffer == 1; pushDoubleBuffering(); for (mAnimTestNr = 0; mAnimTestNr < ANIM_TESTS.length; mAnimTestNr++) { mAnimTests = ANIM_TESTS[mAnimTestNr]; if (!runTestDirs(ANIM_TEST_DIRS)) { return false; } } } return true; } public void testAnimations() { // instead of autoscrolling, load each page until either an timer fires, // or the animation signals complete via javascript setScrollingTestingMode(false); if (checkMedia() && runAnimationTests()) { getInstrumentation().sendStatus(0, mStats.getBundle()); } else { getInstrumentation().sendStatus(1, null); } } }
tests/TileBenchmark/src/com/test/tilebenchmark/PlaybackGraphs.java +80 −27 Original line number Diff line number Diff line Loading @@ -80,7 +80,7 @@ public class PlaybackGraphs { for (int tileID = 1; tileID < frame.length; tileID++) { TileData data = frame[tileID]; double coverage = viewportCoverage(frame[0], data); total += coverage * (data.isReady ? 1 : 0); total += coverage * (data.isReady ? 100 : 0); totalCount += coverage; } if (totalCount == 0) { Loading @@ -91,7 +91,7 @@ public class PlaybackGraphs { @Override public double getMax() { return 1; return 100; } @Override Loading @@ -108,6 +108,9 @@ public class PlaybackGraphs { } public static double getPercentile(double sortedValues[], double ratioAbove) { if (sortedValues.length == 0) return -1; double index = ratioAbove * (sortedValues.length - 1); int intIndex = (int) Math.floor(index); if (index == intIndex) { Loading @@ -118,6 +121,31 @@ public class PlaybackGraphs { + sortedValues[intIndex + 1] * (alpha); } public static double getMean(double sortedValues[]) { if (sortedValues.length == 0) return -1; double agg = 0; for (double val : sortedValues) { agg += val; } return agg / sortedValues.length; } public static double getStdDev(double sortedValues[]) { if (sortedValues.length == 0) return -1; double agg = 0; double sqrAgg = 0; for (double val : sortedValues) { agg += val; sqrAgg += val*val; } double mean = agg / sortedValues.length; return Math.sqrt((sqrAgg / sortedValues.length) - (mean * mean)); } protected static StatGen[] Stats = new StatGen[] { new StatGen() { @Override Loading Loading @@ -149,6 +177,26 @@ public class PlaybackGraphs { public int getLabelId() { return R.string.percentile_75; } }, new StatGen() { @Override public double getValue(double[] sortedValues) { return getStdDev(sortedValues); } @Override public int getLabelId() { return R.string.std_dev; } }, new StatGen() { @Override public double getValue(double[] sortedValues) { return getMean(sortedValues); } @Override public int getLabelId() { return R.string.mean; } }, }; Loading @@ -159,18 +207,10 @@ public class PlaybackGraphs { } private ArrayList<ShapeDrawable> mShapes = new ArrayList<ShapeDrawable>(); protected double[][] mStats = new double[Metrics.length][Stats.length]; protected final double[][] mStats = new double[Metrics.length][Stats.length]; protected HashMap<String, Double> mSingleStats; public void setData(RunData data) { mShapes.clear(); double metricValues[] = new double[data.frames.length]; if (data.frames.length == 0) { return; } for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) { private void gatherFrameMetric(int metricIndex, double metricValues[], RunData data) { // create graph out of rectangles, one per frame int lastBar = 0; for (int frameIndex = 0; frameIndex < data.frames.length; frameIndex++) { Loading @@ -193,6 +233,21 @@ public class PlaybackGraphs { metricValues[frameIndex] = absoluteValue; lastBar = newBar; } } public void setData(RunData data) { mShapes.clear(); double metricValues[] = new double[data.frames.length]; mSingleStats = data.singleStats; if (data.frames.length == 0) { return; } for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) { // calculate metric based on list of frames gatherFrameMetric(metricIndex, metricValues, data); // store aggregate statistics per metric (median, and similar) Arrays.sort(metricValues); Loading @@ -200,8 +255,6 @@ public class PlaybackGraphs { mStats[metricIndex][statIndex] = Stats[statIndex].getValue(metricValues); } mSingleStats = data.singleStats; } } Loading
tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java +49 −20 Original line number Diff line number Diff line Loading @@ -22,11 +22,12 @@ import android.content.Context; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; import android.os.CountDownTimer; import android.util.Log; import android.util.Pair; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.AdapterView; Loading @@ -49,6 +50,8 @@ import java.io.ObjectOutputStream; */ public class ProfileActivity extends Activity { private static final int TIMED_RECORD_MILLIS = 2000; public interface ProfileCallback { public void profileCallback(RunData data); } Loading @@ -65,6 +68,7 @@ public class ProfileActivity extends Activity { LoggingWebViewClient mLoggingWebViewClient = new LoggingWebViewClient(); AutoLoggingWebViewClient mAutoLoggingWebViewClient = new AutoLoggingWebViewClient(); TimedLoggingWebViewClient mTimedLoggingWebViewClient = new TimedLoggingWebViewClient(); private enum TestingState { NOT_TESTING, Loading Loading @@ -93,18 +97,18 @@ public class ProfileActivity extends Activity { public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { String movementStr = parent.getItemAtPosition(position).toString(); if (movementStr == getResources().getString( R.string.movement_auto_scroll) || movementStr == getResources().getString( R.string.movement_auto_fling)) { if (movementStr == getResources().getString(R.string.movement_auto_scroll)) { mWeb.setWebViewClient(mAutoLoggingWebViewClient); mCaptureButton.setEnabled(false); mVelocitySpinner.setEnabled(true); } else if (movementStr == getResources().getString( R.string.movement_manual)) { } else if (movementStr == getResources().getString(R.string.movement_manual)) { mWeb.setWebViewClient(mLoggingWebViewClient); mCaptureButton.setEnabled(true); mVelocitySpinner.setEnabled(false); } else if (movementStr == getResources().getString(R.string.movement_timed)) { mWeb.setWebViewClient(mTimedLoggingWebViewClient); mCaptureButton.setEnabled(false); mVelocitySpinner.setEnabled(false); } } Loading @@ -124,16 +128,46 @@ public class ProfileActivity extends Activity { super.onPageStarted(view, url, favicon); mUrl.setText(url); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); view.requestFocus(); ((ProfiledWebView)view).onPageFinished(); } } private class AutoLoggingWebViewClient extends LoggingWebViewClient { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); startViewProfiling(true); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); setTestingState(TestingState.PRE_TESTING); } } private class TimedLoggingWebViewClient extends LoggingWebViewClient { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); view.requestFocus(); startViewProfiling(false); startViewProfiling(true); // after a fixed time after page finished, stop testing new CountDownTimer(TIMED_RECORD_MILLIS, TIMED_RECORD_MILLIS) { @Override public void onTick(long millisUntilFinished) { } @Override public void onFinish() { mWeb.stopScrollTest(); } }.start(); } @Override Loading Loading @@ -178,11 +212,13 @@ public class ProfileActivity extends Activity { mMovementSpinner.setEnabled(false); break; case START_TESTING: mCaptureButton.setChecked(true); mUrl.setBackgroundResource(R.color.background_start_testing); mInspectButton.setEnabled(false); mMovementSpinner.setEnabled(false); break; case STOP_TESTING: mCaptureButton.setChecked(false); mUrl.setBackgroundResource(R.color.background_stop_testing); break; case SAVED_TESTING: Loading @@ -195,7 +231,6 @@ public class ProfileActivity extends Activity { /** auto - automatically scroll. */ private void startViewProfiling(boolean auto) { // toggle capture button to indicate capture state to user mCaptureButton.setChecked(true); mWeb.startScrollTest(mCallback, auto); setTestingState(TestingState.START_TESTING); } Loading @@ -217,7 +252,7 @@ public class ProfileActivity extends Activity { public void profileCallback(RunData data) { new StoreFileTask().execute(new Pair<String, RunData>( TEMP_FILENAME, data)); mCaptureButton.setChecked(false); Log.d("ProfileActivity", "stored " + data.frames.length + " frames in file"); setTestingState(TestingState.STOP_TESTING); } }); Loading Loading @@ -245,8 +280,8 @@ public class ProfileActivity extends Activity { // Movement spinner String content[] = { getResources().getString(R.string.movement_auto_scroll), getResources().getString(R.string.movement_auto_fling), getResources().getString(R.string.movement_manual) getResources().getString(R.string.movement_manual), getResources().getString(R.string.movement_timed) }; adapter = new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_item, content); Loading @@ -270,13 +305,7 @@ public class ProfileActivity extends Activity { }); // Custom profiling WebView WebSettings settings = mWeb.getSettings(); settings.setJavaScriptEnabled(true); settings.setSupportZoom(true); settings.setEnableSmoothTransition(true); settings.setBuiltInZoomControls(true); settings.setLoadWithOverviewMode(true); settings.setProperty("use_minimal_memory", "false"); // prefetch tiles, as browser does mWeb.init(this); mWeb.setWebViewClient(new LoggingWebViewClient()); // URL text entry Loading
tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java +88 −15 File changed.Preview size limit exceeded, changes collapsed. Show changes