JankBench: make it build and run in Android build

JankBench is a tool heavily used for scheduler and graphics testing.
JankBench has been an android studio project and traditionally its APK
has been built outside of the Android tree using studio. This patch
makes it possible to build it using Android source tree without needing
studio.

Some library imports needed renaming and an xml file had a typo, also
resource IDs need to be 16-bits so I fixed that up. List fragments can't
be anonymous instantiations anymore so changed it to be non-anonymous.

Bug: 31544438
Test: Run all Jankbench benchmarks manually in the app.
Change-Id: Ib5e4351fcc72acdec20424ae30598c205e7803f7
Signed-off-by: Joel Fernandes <joelaf@google.com>
This commit is contained in:
Joel Fernandes
2017-10-26 12:24:30 -07:00
parent 7467d04496
commit b5fbd41b23
79 changed files with 7913 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_MANIFEST_FILE := app/src/main/AndroidManifest.xml
LOCAL_SDK_VERSION := current
LOCAL_USE_AAPT2 := true
# omit gradle 'build' dir
LOCAL_SRC_FILES := $(call all-java-files-under,app/src/main/java)
# use appcompat/support lib from the tree, so improvements/
# regressions are reflected in test data
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/app/src/main/res \
LOCAL_STATIC_ANDROID_LIBRARIES := \
android-support-design \
android-support-v4 \
android-support-v7-appcompat \
android-support-v7-cardview \
android-support-v7-recyclerview \
android-support-v17-leanback \
LOCAL_STATIC_JAVA_LIBRARIES := \
apache-commons-math \
junit
LOCAL_PACKAGE_NAME := JankBench
LOCAL_COMPATIBILITY_SUITE := device-tests
include $(BUILD_PACKAGE)

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.benchmark">
<uses-sdk android:minSdkVersion="24" />
<android:uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
<android:uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".app.HomeActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".app.RunLocalBenchmarksActivity"
android:exported="true">
<intent-filter>
<action android:name="com.android.benchmark.ACTION_BENCHMARK" />
</intent-filter>
<meta-data
android:name="com.android.benchmark.benchmark_group"
android:resource="@xml/benchmark" />
</activity>
<activity android:name=".ui.ListViewScrollActivity" />
<activity android:name=".ui.ImageListViewScrollActivity" />
<activity android:name=".ui.ShadowGridActivity" />
<activity android:name=".ui.TextScrollActivity" />
<activity android:name=".ui.EditTextInputActivity" />
<activity android:name=".synthetic.MemoryActivity" />
<activity android:name=".ui.FullScreenOverdrawActivity"></activity>
<activity android:name=".ui.BitmapUploadActivity"></activity>
</application>
</manifest>

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.app;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.benchmark.R;
/**
* Fragment for the Benchmark dashboard
*/
public class BenchmarkDashboardFragment extends Fragment {
public BenchmarkDashboardFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_dashboard, container, false);
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.app;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckBox;
import android.widget.TextView;
import com.android.benchmark.registry.BenchmarkGroup;
import com.android.benchmark.registry.BenchmarkRegistry;
import com.android.benchmark.R;
/**
*
*/
public class BenchmarkListAdapter extends BaseExpandableListAdapter {
private final LayoutInflater mInflater;
private final BenchmarkRegistry mRegistry;
BenchmarkListAdapter(LayoutInflater inflater,
BenchmarkRegistry registry) {
mInflater = inflater;
mRegistry = registry;
}
@Override
public int getGroupCount() {
return mRegistry.getGroupCount();
}
@Override
public int getChildrenCount(int groupPosition) {
return mRegistry.getBenchmarkCount(groupPosition);
}
@Override
public Object getGroup(int groupPosition) {
return mRegistry.getBenchmarkGroup(groupPosition);
}
@Override
public Object getChild(int groupPosition, int childPosition) {
BenchmarkGroup benchmarkGroup = mRegistry.getBenchmarkGroup(groupPosition);
if (benchmarkGroup != null) {
return benchmarkGroup.getBenchmarks()[childPosition];
}
return null;
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
BenchmarkGroup group = (BenchmarkGroup) getGroup(groupPosition);
if (convertView == null) {
convertView = mInflater.inflate(R.layout.benchmark_list_group_row, null);
}
TextView title = (TextView) convertView.findViewById(R.id.group_name);
title.setTypeface(null, Typeface.BOLD);
title.setText(group.getTitle());
return convertView;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
BenchmarkGroup.Benchmark benchmark =
(BenchmarkGroup.Benchmark) getChild(groupPosition, childPosition);
if (convertView == null) {
convertView = mInflater.inflate(R.layout.benchmark_list_item, null);
}
TextView name = (TextView) convertView.findViewById(R.id.benchmark_name);
name.setText(benchmark.getName());
CheckBox enabledBox = (CheckBox) convertView.findViewById(R.id.benchmark_enable_checkbox);
enabledBox.setOnClickListener(benchmark);
enabledBox.setChecked(benchmark.isEnabled());
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public int getChildrenHeight() {
// TODO
return 1024;
}
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.app;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ExpandableListView;
import android.widget.Toast;
import com.android.benchmark.registry.BenchmarkRegistry;
import com.android.benchmark.R;
import com.android.benchmark.results.GlobalResultsStore;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
public class HomeActivity extends AppCompatActivity implements Button.OnClickListener {
private FloatingActionButton mStartButton;
private BenchmarkRegistry mRegistry;
private Queue<Intent> mRunnableBenchmarks;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mStartButton = (FloatingActionButton) findViewById(R.id.start_button);
mStartButton.setActivated(true);
mStartButton.setOnClickListener(this);
mRegistry = new BenchmarkRegistry(this);
mRunnableBenchmarks = new LinkedList<>();
ExpandableListView listView = (ExpandableListView) findViewById(R.id.test_list);
BenchmarkListAdapter adapter =
new BenchmarkListAdapter(LayoutInflater.from(this), mRegistry);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
ViewGroup.LayoutParams layoutParams = listView.getLayoutParams();
layoutParams.height = 2048;
listView.setLayoutParams(layoutParams);
listView.requestLayout();
System.out.println(System.getProperties().stringPropertyNames());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
try {
HomeActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(HomeActivity.this, "Exporting...", Toast.LENGTH_LONG).show();
}
});
GlobalResultsStore.getInstance(HomeActivity.this).exportToCsv();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
HomeActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(HomeActivity.this, "Done", Toast.LENGTH_LONG).show();
}
});
}
}.execute();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onClick(View v) {
final int groupCount = mRegistry.getGroupCount();
for (int i = 0; i < groupCount; i++) {
Intent intent = mRegistry.getBenchmarkGroup(i).getIntent();
if (intent != null) {
mRunnableBenchmarks.add(intent);
}
}
handleNextBenchmark();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
}
private void handleNextBenchmark() {
Intent nextIntent = mRunnableBenchmarks.peek();
startActivityForResult(nextIntent, 0);
}
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.app;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.*;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
import com.android.benchmark.R;
/**
* TODO: document your custom view class.
*/
public class PerfTimeline extends View {
private String mExampleString; // TODO: use a default from R.string...
private int mExampleColor = Color.RED; // TODO: use a default from R.color...
private float mExampleDimension = 300; // TODO: use a default from R.dimen...
private TextPaint mTextPaint;
private float mTextWidth;
private float mTextHeight;
private Paint mPaintBaseLow;
private Paint mPaintBaseHigh;
private Paint mPaintValue;
public float[] mLinesLow;
public float[] mLinesHigh;
public float[] mLinesValue;
public PerfTimeline(Context context) {
super(context);
init(null, 0);
}
public PerfTimeline(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public PerfTimeline(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
// Load attributes
final TypedArray a = getContext().obtainStyledAttributes(
attrs, R.styleable.PerfTimeline, defStyle, 0);
mExampleString = "xx";//a.getString(R.styleable.PerfTimeline_exampleString, "xx");
mExampleColor = a.getColor(R.styleable.PerfTimeline_exampleColor, mExampleColor);
// Use getDimensionPixelSize or getDimensionPixelOffset when dealing with
// values that should fall on pixel boundaries.
mExampleDimension = a.getDimension(
R.styleable.PerfTimeline_exampleDimension,
mExampleDimension);
a.recycle();
// Set up a default TextPaint object
mTextPaint = new TextPaint();
mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextAlign(Paint.Align.LEFT);
// Update TextPaint and text measurements from attributes
invalidateTextPaintAndMeasurements();
mPaintBaseLow = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintBaseLow.setStyle(Paint.Style.FILL);
mPaintBaseLow.setColor(0xff000000);
mPaintBaseHigh = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintBaseHigh.setStyle(Paint.Style.FILL);
mPaintBaseHigh.setColor(0x7f7f7f7f);
mPaintValue = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintValue.setStyle(Paint.Style.FILL);
mPaintValue.setColor(0x7fff0000);
}
private void invalidateTextPaintAndMeasurements() {
mTextPaint.setTextSize(mExampleDimension);
mTextPaint.setColor(mExampleColor);
mTextWidth = mTextPaint.measureText(mExampleString);
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
mTextHeight = fontMetrics.bottom;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// TODO: consider storing these as member variables to reduce
// allocations per draw cycle.
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int contentWidth = getWidth() - paddingLeft - paddingRight;
int contentHeight = getHeight() - paddingTop - paddingBottom;
// Draw the text.
//canvas.drawText(mExampleString,
// paddingLeft + (contentWidth - mTextWidth) / 2,
// paddingTop + (contentHeight + mTextHeight) / 2,
// mTextPaint);
// Draw the shadow
//RectF rf = new RectF(10.f, 10.f, 100.f, 100.f);
//canvas.drawOval(rf, mShadowPaint);
if (mLinesLow != null) {
canvas.drawLines(mLinesLow, mPaintBaseLow);
}
if (mLinesHigh != null) {
canvas.drawLines(mLinesHigh, mPaintBaseHigh);
}
if (mLinesValue != null) {
canvas.drawLines(mLinesValue, mPaintValue);
}
/*
// Draw the pie slices
for (int i = 0; i < mData.size(); ++i) {
Item it = mData.get(i);
mPiePaint.setShader(it.mShader);
canvas.drawArc(mBounds,
360 - it.mEndAngle,
it.mEndAngle - it.mStartAngle,
true, mPiePaint);
}
*/
// Draw the pointer
//canvas.drawLine(mTextX, mPointerY, mPointerX, mPointerY, mTextPaint);
//canvas.drawCircle(mPointerX, mPointerY, mPointerSize, mTextPaint);
}
/**
* Gets the example string attribute value.
*
* @return The example string attribute value.
*/
public String getExampleString() {
return mExampleString;
}
/**
* Sets the view's example string attribute value. In the example view, this string
* is the text to draw.
*
* @param exampleString The example string attribute value to use.
*/
public void setExampleString(String exampleString) {
mExampleString = exampleString;
invalidateTextPaintAndMeasurements();
}
/**
* Gets the example color attribute value.
*
* @return The example color attribute value.
*/
public int getExampleColor() {
return mExampleColor;
}
/**
* Sets the view's example color attribute value. In the example view, this color
* is the font color.
*
* @param exampleColor The example color attribute value to use.
*/
public void setExampleColor(int exampleColor) {
mExampleColor = exampleColor;
invalidateTextPaintAndMeasurements();
}
/**
* Gets the example dimension attribute value.
*
* @return The example dimension attribute value.
*/
public float getExampleDimension() {
return mExampleDimension;
}
/**
* Sets the view's example dimension attribute value. In the example view, this dimension
* is the font size.
*
* @param exampleDimension The example dimension attribute value to use.
*/
public void setExampleDimension(float exampleDimension) {
mExampleDimension = exampleDimension;
invalidateTextPaintAndMeasurements();
}
}

View File

@@ -0,0 +1,415 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.app;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.ListFragment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.android.benchmark.R;
import com.android.benchmark.registry.BenchmarkGroup;
import com.android.benchmark.registry.BenchmarkRegistry;
import com.android.benchmark.results.GlobalResultsStore;
import com.android.benchmark.results.UiBenchmarkResult;
import com.android.benchmark.synthetic.MemoryActivity;
import com.android.benchmark.ui.BitmapUploadActivity;
import com.android.benchmark.ui.EditTextInputActivity;
import com.android.benchmark.ui.FullScreenOverdrawActivity;
import com.android.benchmark.ui.ImageListViewScrollActivity;
import com.android.benchmark.ui.ListViewScrollActivity;
import com.android.benchmark.ui.ShadowGridActivity;
import com.android.benchmark.ui.TextScrollActivity;
import org.apache.commons.math.stat.descriptive.SummaryStatistics;
import java.util.ArrayList;
import java.util.HashMap;
public class RunLocalBenchmarksActivity extends AppCompatActivity {
public static final int RUN_COUNT = 5;
private ArrayList<LocalBenchmark> mBenchmarksToRun;
private int mBenchmarkCursor;
private int mCurrentRunId;
private boolean mFinish;
private Handler mHandler = new Handler();
private static final int[] ALL_TESTS = new int[] {
R.id.benchmark_list_view_scroll,
R.id.benchmark_image_list_view_scroll,
R.id.benchmark_shadow_grid,
R.id.benchmark_text_high_hitrate,
R.id.benchmark_text_low_hitrate,
R.id.benchmark_edit_text_input,
R.id.benchmark_overdraw,
};
public static class LocalBenchmarksList extends ListFragment {
private ArrayList<LocalBenchmark> mBenchmarks;
private int mRunId;
public void setBenchmarks(ArrayList<LocalBenchmark> benchmarks) {
mBenchmarks = benchmarks;
}
public void setRunId(int id) {
mRunId = id;
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
if (getActivity().findViewById(R.id.list_fragment_container) != null) {
FragmentManager fm = getActivity().getSupportFragmentManager();
UiResultsFragment resultsView = new UiResultsFragment();
String testName = BenchmarkRegistry.getBenchmarkName(v.getContext(),
mBenchmarks.get(position).id);
resultsView.setRunInfo(testName, mRunId);
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(R.id.list_fragment_container, resultsView);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
}
private class LocalBenchmark {
int id;
int runCount = 0;
int totalCount = 0;
ArrayList<String> mResultsUri = new ArrayList<>();
LocalBenchmark(int id, int runCount) {
this.id = id;
this.runCount = 0;
this.totalCount = runCount;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_running_list);
initLocalBenchmarks(getIntent());
if (findViewById(R.id.list_fragment_container) != null) {
FragmentManager fm = getSupportFragmentManager();
LocalBenchmarksList listView = new LocalBenchmarksList();
listView.setListAdapter(new LocalBenchmarksListAdapter(LayoutInflater.from(this)));
listView.setBenchmarks(mBenchmarksToRun);
listView.setRunId(mCurrentRunId);
fm.beginTransaction().add(R.id.list_fragment_container, listView).commit();
}
TextView scoreView = (TextView) findViewById(R.id.score_text_view);
scoreView.setText("Running tests!");
}
private int translateBenchmarkIndex(int index) {
if (index >= 0 && index < ALL_TESTS.length) {
return ALL_TESTS[index];
}
return -1;
}
private void initLocalBenchmarks(Intent intent) {
mBenchmarksToRun = new ArrayList<>();
int[] enabledIds = intent.getIntArrayExtra(BenchmarkGroup.BENCHMARK_EXTRA_ENABLED_TESTS);
int runCount = intent.getIntExtra(BenchmarkGroup.BENCHMARK_EXTRA_RUN_COUNT, RUN_COUNT);
mFinish = intent.getBooleanExtra(BenchmarkGroup.BENCHMARK_EXTRA_FINISH, false);
if (enabledIds == null) {
// run all tests
enabledIds = ALL_TESTS;
}
StringBuilder idString = new StringBuilder();
idString.append(runCount);
idString.append(System.currentTimeMillis());
for (int i = 0; i < enabledIds.length; i++) {
int id = enabledIds[i];
System.out.println("considering " + id);
if (!isValidBenchmark(id)) {
System.out.println("not valid " + id);
id = translateBenchmarkIndex(id);
System.out.println("got out " + id);
System.out.println("expected: " + R.id.benchmark_overdraw);
}
if (isValidBenchmark(id)) {
int localRunCount = runCount;
if (isCompute(id)) {
localRunCount = 1;
}
mBenchmarksToRun.add(new LocalBenchmark(id, localRunCount));
idString.append(id);
}
}
mBenchmarkCursor = 0;
mCurrentRunId = idString.toString().hashCode();
}
private boolean isCompute(int id) {
switch (id) {
case R.id.benchmark_cpu_gflops:
case R.id.benchmark_cpu_heat_soak:
case R.id.benchmark_memory_bandwidth:
case R.id.benchmark_memory_latency:
case R.id.benchmark_power_management:
return true;
default:
return false;
}
}
private static boolean isValidBenchmark(int benchmarkId) {
switch (benchmarkId) {
case R.id.benchmark_list_view_scroll:
case R.id.benchmark_image_list_view_scroll:
case R.id.benchmark_shadow_grid:
case R.id.benchmark_text_high_hitrate:
case R.id.benchmark_text_low_hitrate:
case R.id.benchmark_edit_text_input:
case R.id.benchmark_overdraw:
case R.id.benchmark_memory_bandwidth:
case R.id.benchmark_memory_latency:
case R.id.benchmark_power_management:
case R.id.benchmark_cpu_heat_soak:
case R.id.benchmark_cpu_gflops:
return true;
default:
return false;
}
}
@Override
protected void onResume() {
super.onResume();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
runNextBenchmark();
}
}, 1000);
}
private void computeOverallScore() {
final TextView scoreView = (TextView) findViewById(R.id.score_text_view);
scoreView.setText("Computing score...");
new AsyncTask<Void, Void, Integer>() {
@Override
protected Integer doInBackground(Void... voids) {
GlobalResultsStore gsr =
GlobalResultsStore.getInstance(RunLocalBenchmarksActivity.this);
ArrayList<Double> testLevelScores = new ArrayList<>();
final SummaryStatistics stats = new SummaryStatistics();
for (LocalBenchmark b : mBenchmarksToRun) {
HashMap<String, ArrayList<UiBenchmarkResult>> detailedResults =
gsr.loadDetailedResults(mCurrentRunId);
for (ArrayList<UiBenchmarkResult> testResult : detailedResults.values()) {
for (UiBenchmarkResult res : testResult) {
int score = res.getScore();
if (score == 0) {
score = 1;
}
stats.addValue(score);
}
testLevelScores.add(stats.getGeometricMean());
stats.clear();
}
}
for (double score : testLevelScores) {
stats.addValue(score);
}
return (int)Math.round(stats.getGeometricMean());
}
@Override
protected void onPostExecute(Integer score) {
TextView view = (TextView)
RunLocalBenchmarksActivity.this.findViewById(R.id.score_text_view);
view.setText("Score: " + score);
}
}.execute();
}
private void runNextBenchmark() {
LocalBenchmark benchmark = mBenchmarksToRun.get(mBenchmarkCursor);
boolean runAgain = false;
if (benchmark.runCount < benchmark.totalCount) {
runBenchmarkForId(mBenchmarksToRun.get(mBenchmarkCursor).id, benchmark.runCount++);
} else if (mBenchmarkCursor + 1 < mBenchmarksToRun.size()) {
mBenchmarkCursor++;
benchmark = mBenchmarksToRun.get(mBenchmarkCursor);
runBenchmarkForId(benchmark.id, benchmark.runCount++);
} else if (runAgain) {
mBenchmarkCursor = 0;
initLocalBenchmarks(getIntent());
runBenchmarkForId(mBenchmarksToRun.get(mBenchmarkCursor).id, benchmark.runCount);
} else if (mFinish) {
finish();
} else {
Log.i("BENCH", "BenchmarkDone!");
computeOverallScore();
}
}
private void runBenchmarkForId(int id, int iteration) {
Intent intent;
int syntheticTestId = -1;
System.out.println("iteration: " + iteration);
switch (id) {
case R.id.benchmark_list_view_scroll:
intent = new Intent(getApplicationContext(), ListViewScrollActivity.class);
break;
case R.id.benchmark_image_list_view_scroll:
intent = new Intent(getApplicationContext(), ImageListViewScrollActivity.class);
break;
case R.id.benchmark_shadow_grid:
intent = new Intent(getApplicationContext(), ShadowGridActivity.class);
break;
case R.id.benchmark_text_high_hitrate:
intent = new Intent(getApplicationContext(), TextScrollActivity.class);
intent.putExtra(TextScrollActivity.EXTRA_HIT_RATE, 80);
intent.putExtra(BenchmarkRegistry.EXTRA_ID, id);
break;
case R.id.benchmark_text_low_hitrate:
intent = new Intent(getApplicationContext(), TextScrollActivity.class);
intent.putExtra(TextScrollActivity.EXTRA_HIT_RATE, 20);
intent.putExtra(BenchmarkRegistry.EXTRA_ID, id);
break;
case R.id.benchmark_edit_text_input:
intent = new Intent(getApplicationContext(), EditTextInputActivity.class);
break;
case R.id.benchmark_overdraw:
intent = new Intent(getApplicationContext(), BitmapUploadActivity.class);
break;
case R.id.benchmark_memory_bandwidth:
syntheticTestId = 0;
intent = new Intent(getApplicationContext(), MemoryActivity.class);
intent.putExtra("test", syntheticTestId);
break;
case R.id.benchmark_memory_latency:
syntheticTestId = 1;
intent = new Intent(getApplicationContext(), MemoryActivity.class);
intent.putExtra("test", syntheticTestId);
break;
case R.id.benchmark_power_management:
syntheticTestId = 2;
intent = new Intent(getApplicationContext(), MemoryActivity.class);
intent.putExtra("test", syntheticTestId);
break;
case R.id.benchmark_cpu_heat_soak:
syntheticTestId = 3;
intent = new Intent(getApplicationContext(), MemoryActivity.class);
intent.putExtra("test", syntheticTestId);
break;
case R.id.benchmark_cpu_gflops:
syntheticTestId = 4;
intent = new Intent(getApplicationContext(), MemoryActivity.class);
intent.putExtra("test", syntheticTestId);
break;
default:
intent = null;
}
if (intent != null) {
intent.putExtra("com.android.benchmark.RUN_ID", mCurrentRunId);
intent.putExtra("com.android.benchmark.ITERATION", iteration);
startActivityForResult(intent, id & 0xffff, null);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case R.id.benchmark_shadow_grid:
case R.id.benchmark_list_view_scroll:
case R.id.benchmark_image_list_view_scroll:
case R.id.benchmark_text_high_hitrate:
case R.id.benchmark_text_low_hitrate:
case R.id.benchmark_edit_text_input:
break;
default:
}
}
class LocalBenchmarksListAdapter extends BaseAdapter {
private final LayoutInflater mInflater;
LocalBenchmarksListAdapter(LayoutInflater inflater) {
mInflater = inflater;
}
@Override
public int getCount() {
return mBenchmarksToRun.size();
}
@Override
public Object getItem(int i) {
return mBenchmarksToRun.get(i);
}
@Override
public long getItemId(int i) {
return mBenchmarksToRun.get(i).id;
}
@Override
public View getView(int i, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.running_benchmark_list_item, null);
}
TextView name = (TextView) convertView.findViewById(R.id.benchmark_name);
name.setText(BenchmarkRegistry.getBenchmarkName(
RunLocalBenchmarksActivity.this, mBenchmarksToRun.get(i).id));
return convertView;
}
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.app;
import android.annotation.TargetApi;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.ListFragment;
import android.util.Log;
import android.view.FrameMetrics;
import android.widget.SimpleAdapter;
import com.android.benchmark.R;
import com.android.benchmark.registry.BenchmarkGroup;
import com.android.benchmark.registry.BenchmarkRegistry;
import com.android.benchmark.results.GlobalResultsStore;
import com.android.benchmark.results.UiBenchmarkResult;
import org.apache.commons.math.stat.descriptive.SummaryStatistics;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@TargetApi(24)
public class UiResultsFragment extends ListFragment {
private static final String TAG = "UiResultsFragment";
private static final int NUM_FIELDS = 20;
private ArrayList<UiBenchmarkResult> mResults = new ArrayList<>();
private AsyncTask<Void, Void, ArrayList<Map<String, String>>> mLoadScoresTask =
new AsyncTask<Void, Void, ArrayList<Map<String, String>>>() {
@Override
protected ArrayList<Map<String, String>> doInBackground(Void... voids) {
String[] data;
if (mResults.size() == 0 || mResults.get(0) == null) {
data = new String[] {
"No metrics reported", ""
};
} else {
data = new String[NUM_FIELDS * (1 + mResults.size()) + 2];
SummaryStatistics stats = new SummaryStatistics();
int totalFrameCount = 0;
double totalAvgFrameDuration = 0;
double total99FrameDuration = 0;
double total95FrameDuration = 0;
double total90FrameDuration = 0;
double totalLongestFrame = 0;
double totalShortestFrame = 0;
for (int i = 0; i < mResults.size(); i++) {
int start = (i * NUM_FIELDS) + + NUM_FIELDS;
data[(start++)] = "Iteration";
data[(start++)] = "" + i;
data[(start++)] = "Total Frames";
int currentFrameCount = mResults.get(i).getTotalFrameCount();
totalFrameCount += currentFrameCount;
data[(start++)] = Integer.toString(currentFrameCount);
data[(start++)] = "Average frame duration:";
double currentAvgFrameDuration = mResults.get(i).getAverage(FrameMetrics.TOTAL_DURATION);
totalAvgFrameDuration += currentAvgFrameDuration;
data[(start++)] = String.format("%.2f", currentAvgFrameDuration);
data[(start++)] = "Frame duration 99th:";
double current99FrameDuration = mResults.get(i).getPercentile(FrameMetrics.TOTAL_DURATION, 99);
total99FrameDuration += current99FrameDuration;
data[(start++)] = String.format("%.2f", current99FrameDuration);
data[(start++)] = "Frame duration 95th:";
double current95FrameDuration = mResults.get(i).getPercentile(FrameMetrics.TOTAL_DURATION, 95);
total95FrameDuration += current95FrameDuration;
data[(start++)] = String.format("%.2f", current95FrameDuration);
data[(start++)] = "Frame duration 90th:";
double current90FrameDuration = mResults.get(i).getPercentile(FrameMetrics.TOTAL_DURATION, 90);
total90FrameDuration += current90FrameDuration;
data[(start++)] = String.format("%.2f", current90FrameDuration);
data[(start++)] = "Longest frame:";
double longestFrame = mResults.get(i).getMaximum(FrameMetrics.TOTAL_DURATION);
if (totalLongestFrame == 0 || longestFrame > totalLongestFrame) {
totalLongestFrame = longestFrame;
}
data[(start++)] = String.format("%.2f", longestFrame);
data[(start++)] = "Shortest frame:";
double shortestFrame = mResults.get(i).getMinimum(FrameMetrics.TOTAL_DURATION);
if (totalShortestFrame == 0 || totalShortestFrame > shortestFrame) {
totalShortestFrame = shortestFrame;
}
data[(start++)] = String.format("%.2f", shortestFrame);
data[(start++)] = "Score:";
double score = mResults.get(i).getScore();
stats.addValue(score);
data[(start++)] = String.format("%.2f", score);
data[(start++)] = "==============";
data[(start++)] = "============================";
};
int start = 0;
data[0] = "Overall: ";
data[1] = "";
data[(start++)] = "Total Frames";
data[(start++)] = Integer.toString(totalFrameCount);
data[(start++)] = "Average frame duration:";
data[(start++)] = String.format("%.2f", totalAvgFrameDuration / mResults.size());
data[(start++)] = "Frame duration 99th:";
data[(start++)] = String.format("%.2f", total99FrameDuration / mResults.size());
data[(start++)] = "Frame duration 95th:";
data[(start++)] = String.format("%.2f", total95FrameDuration / mResults.size());
data[(start++)] = "Frame duration 90th:";
data[(start++)] = String.format("%.2f", total90FrameDuration / mResults.size());
data[(start++)] = "Longest frame:";
data[(start++)] = String.format("%.2f", totalLongestFrame);
data[(start++)] = "Shortest frame:";
data[(start++)] = String.format("%.2f", totalShortestFrame);
data[(start++)] = "Score:";
data[(start++)] = String.format("%.2f", stats.getGeometricMean());
data[(start++)] = "==============";
data[(start++)] = "============================";
}
ArrayList<Map<String, String>> dataMap = new ArrayList<>();
for (int i = 0; i < data.length - 1; i += 2) {
HashMap<String, String> map = new HashMap<>();
map.put("name", data[i]);
map.put("value", data[i + 1]);
dataMap.add(map);
}
return dataMap;
}
@Override
protected void onPostExecute(ArrayList<Map<String, String>> dataMap) {
setListAdapter(new SimpleAdapter(getActivity(), dataMap, R.layout.results_list_item,
new String[] {"name", "value"}, new int[] { R.id.result_name, R.id.result_value }));
setListShown(true);
}
};
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListShown(false);
mLoadScoresTask.execute();
}
public void setRunInfo(String name, int runId) {
mResults = GlobalResultsStore.getInstance(getActivity()).loadTestResults(name, runId);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.registry;
import android.support.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Represents the category of a particular benchmark.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({BenchmarkCategory.GENERIC, BenchmarkCategory.UI, BenchmarkCategory.COMPUTE})
@interface BenchmarkCategory {
int GENERIC = 0;
int UI = 1;
int COMPUTE = 2;
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.registry;
import android.content.ComponentName;
import android.content.Intent;
import android.view.View;
import android.widget.CheckBox;
/**
* Logical grouping of benchmarks
*/
public class BenchmarkGroup {
public static final String BENCHMARK_EXTRA_ENABLED_TESTS =
"com.android.benchmark.EXTRA_ENABLED_BENCHMARK_IDS";
public static final String BENCHMARK_EXTRA_RUN_COUNT =
"com.android.benchmark.EXTRA_RUN_COUNT";
public static final String BENCHMARK_EXTRA_FINISH = "com.android.benchmark.FINISH_WHEN_DONE";
public static class Benchmark implements CheckBox.OnClickListener {
/** The name of this individual benchmark test */
private final String mName;
/** The category of this individual benchmark test */
private final @BenchmarkCategory int mCategory;
/** Human-readable description of the benchmark */
private final String mDescription;
private final int mId;
private boolean mEnabled;
Benchmark(int id, String name, @BenchmarkCategory int category, String description) {
mId = id;
mName = name;
mCategory = category;
mDescription = description;
mEnabled = true;
}
public boolean isEnabled() { return mEnabled; }
public void setEnabled(boolean enabled) { mEnabled = enabled; }
public int getId() { return mId; }
public String getDescription() { return mDescription; }
public @BenchmarkCategory int getCategory() { return mCategory; }
public String getName() { return mName; }
@Override
public void onClick(View view) {
setEnabled(((CheckBox)view).isChecked());
}
}
/**
* Component for this benchmark group.
*/
private final ComponentName mComponentName;
/**
* Benchmark title, showed in the {@link android.widget.ListView}
*/
private final String mTitle;
/**
* List of all benchmarks exported by this group
*/
private final Benchmark[] mBenchmarks;
/**
* The intent to launch the benchmark
*/
private final Intent mIntent;
/** Human-readable description of the benchmark group */
private final String mDescription;
BenchmarkGroup(ComponentName componentName, String title,
String description, Benchmark[] benchmarks, Intent intent) {
mComponentName = componentName;
mTitle = title;
mBenchmarks = benchmarks;
mDescription = description;
mIntent = intent;
}
public Intent getIntent() {
int[] enabledBenchmarksIds = getEnabledBenchmarksIds();
if (enabledBenchmarksIds.length != 0) {
mIntent.putExtra(BENCHMARK_EXTRA_ENABLED_TESTS, enabledBenchmarksIds);
return mIntent;
}
return null;
}
public ComponentName getComponentName() {
return mComponentName;
}
public String getTitle() {
return mTitle;
}
public Benchmark[] getBenchmarks() {
return mBenchmarks;
}
public String getDescription() {
return mDescription;
}
private int[] getEnabledBenchmarksIds() {
int enabledBenchmarkCount = 0;
for (int i = 0; i < mBenchmarks.length; i++) {
if (mBenchmarks[i].isEnabled()) {
enabledBenchmarkCount++;
}
}
int writeIndex = 0;
int[] enabledBenchmarks = new int[enabledBenchmarkCount];
for (int i = 0; i < mBenchmarks.length; i++) {
if (mBenchmarks[i].isEnabled()) {
enabledBenchmarks[writeIndex++] = mBenchmarks[i].getId();
}
}
return enabledBenchmarks;
}
}

View File

@@ -0,0 +1,236 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.registry;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.util.Xml;
import com.android.benchmark.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
*/
public class BenchmarkRegistry {
/** Metadata key for benchmark XML data */
private static final String BENCHMARK_GROUP_META_KEY =
"com.android.benchmark.benchmark_group";
/** Intent action specifying an activity that runs a single benchmark test. */
private static final String ACTION_BENCHMARK = "com.android.benchmark.ACTION_BENCHMARK";
public static final String EXTRA_ID = "com.android.benchmark.EXTRA_ID";
private static final String TAG_BENCHMARK_GROUP = "com.android.benchmark.BenchmarkGroup";
private static final String TAG_BENCHMARK = "com.android.benchmark.Benchmark";
private List<BenchmarkGroup> mGroups;
private final Context mContext;
public BenchmarkRegistry(Context context) {
mContext = context;
mGroups = new ArrayList<>();
loadBenchmarks();
}
private Intent getIntentFromInfo(ActivityInfo inf) {
Intent intent = new Intent();
intent.setClassName(inf.packageName, inf.name);
return intent;
}
public void loadBenchmarks() {
Intent intent = new Intent(ACTION_BENCHMARK);
intent.setPackage(mContext.getPackageName());
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent,
PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
for (ResolveInfo inf : resolveInfos) {
List<BenchmarkGroup> groups = parseBenchmarkGroup(inf.activityInfo);
if (groups != null) {
mGroups.addAll(groups);
}
}
}
private boolean seekToTag(XmlPullParser parser, String tag)
throws XmlPullParserException, IOException {
int eventType = parser.getEventType();
while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) {
eventType = parser.next();
}
return eventType != XmlPullParser.END_DOCUMENT && tag.equals(parser.getName());
}
@BenchmarkCategory int getCategory(int category) {
switch (category) {
case BenchmarkCategory.COMPUTE:
return BenchmarkCategory.COMPUTE;
case BenchmarkCategory.UI:
return BenchmarkCategory.UI;
default:
return BenchmarkCategory.GENERIC;
}
}
private List<BenchmarkGroup> parseBenchmarkGroup(ActivityInfo activityInfo) {
PackageManager pm = mContext.getPackageManager();
ComponentName componentName = new ComponentName(
activityInfo.packageName, activityInfo.name);
SparseArray<List<BenchmarkGroup.Benchmark>> benchmarks = new SparseArray<>();
String groupName, groupDescription;
try (XmlResourceParser parser = activityInfo.loadXmlMetaData(pm, BENCHMARK_GROUP_META_KEY)) {
if (!seekToTag(parser, TAG_BENCHMARK_GROUP)) {
return null;
}
Resources res = pm.getResourcesForActivity(componentName);
AttributeSet attributeSet = Xml.asAttributeSet(parser);
TypedArray groupAttribs = res.obtainAttributes(attributeSet, R.styleable.BenchmarkGroup);
groupName = groupAttribs.getString(R.styleable.BenchmarkGroup_name);
groupDescription = groupAttribs.getString(R.styleable.BenchmarkGroup_description);
groupAttribs.recycle();
parser.next();
while (seekToTag(parser, TAG_BENCHMARK)) {
TypedArray benchAttribs =
res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Benchmark);
int id = benchAttribs.getResourceId(R.styleable.Benchmark_id, -1);
String testName = benchAttribs.getString(R.styleable.Benchmark_name);
String testDescription = benchAttribs.getString(R.styleable.Benchmark_description);
int testCategory = benchAttribs.getInt(R.styleable.Benchmark_category,
BenchmarkCategory.GENERIC);
int category = getCategory(testCategory);
BenchmarkGroup.Benchmark benchmark = new BenchmarkGroup.Benchmark(
id, testName, category, testDescription);
List<BenchmarkGroup.Benchmark> benches = benchmarks.get(category);
if (benches == null) {
benches = new ArrayList<>();
benchmarks.append(category, benches);
}
benches.add(benchmark);
benchAttribs.recycle();
parser.next();
}
} catch (PackageManager.NameNotFoundException | XmlPullParserException | IOException e) {
return null;
}
List<BenchmarkGroup> result = new ArrayList<>();
Intent testIntent = getIntentFromInfo(activityInfo);
for (int i = 0; i < benchmarks.size(); i++) {
int cat = benchmarks.keyAt(i);
List<BenchmarkGroup.Benchmark> thisGroup = benchmarks.get(cat);
BenchmarkGroup.Benchmark[] benchmarkArray =
new BenchmarkGroup.Benchmark[thisGroup.size()];
thisGroup.toArray(benchmarkArray);
result.add(new BenchmarkGroup(componentName,
groupName + " - " + getCategoryString(cat), groupDescription, benchmarkArray,
testIntent));
}
return result;
}
public int getGroupCount() {
return mGroups.size();
}
public int getBenchmarkCount(int benchmarkIndex) {
BenchmarkGroup group = getBenchmarkGroup(benchmarkIndex);
if (group != null) {
return group.getBenchmarks().length;
}
return 0;
}
public BenchmarkGroup getBenchmarkGroup(int benchmarkIndex) {
if (benchmarkIndex >= mGroups.size()) {
return null;
}
return mGroups.get(benchmarkIndex);
}
public static String getCategoryString(int category) {
switch (category) {
case BenchmarkCategory.UI:
return "UI";
case BenchmarkCategory.COMPUTE:
return "Compute";
case BenchmarkCategory.GENERIC:
return "Generic";
default:
return "";
}
}
public static String getBenchmarkName(Context context, int benchmarkId) {
switch (benchmarkId) {
case R.id.benchmark_list_view_scroll:
return context.getString(R.string.list_view_scroll_name);
case R.id.benchmark_image_list_view_scroll:
return context.getString(R.string.image_list_view_scroll_name);
case R.id.benchmark_shadow_grid:
return context.getString(R.string.shadow_grid_name);
case R.id.benchmark_text_high_hitrate:
return context.getString(R.string.text_high_hitrate_name);
case R.id.benchmark_text_low_hitrate:
return context.getString(R.string.text_low_hitrate_name);
case R.id.benchmark_edit_text_input:
return context.getString(R.string.edit_text_input_name);
case R.id.benchmark_memory_bandwidth:
return context.getString(R.string.memory_bandwidth_name);
case R.id.benchmark_memory_latency:
return context.getString(R.string.memory_latency_name);
case R.id.benchmark_power_management:
return context.getString(R.string.power_management_name);
case R.id.benchmark_cpu_heat_soak:
return context.getString(R.string.cpu_heat_soak_name);
case R.id.benchmark_cpu_gflops:
return context.getString(R.string.cpu_gflops_name);
case R.id.benchmark_overdraw:
return context.getString(R.string.overdraw_name);
default:
return "Some Benchmark";
}
}
}

View File

@@ -0,0 +1,389 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.results;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.view.FrameMetrics;
import android.widget.Toast;
import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
public class GlobalResultsStore extends SQLiteOpenHelper {
private static final int VERSION = 2;
private static GlobalResultsStore sInstance;
private static final String UI_RESULTS_TABLE = "ui_results";
private final Context mContext;
private GlobalResultsStore(Context context) {
super(context, "BenchmarkResults", null, VERSION);
mContext = context;
}
public static GlobalResultsStore getInstance(Context context) {
if (sInstance == null) {
sInstance = new GlobalResultsStore(context.getApplicationContext());
}
return sInstance;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL("CREATE TABLE " + UI_RESULTS_TABLE + " (" +
" _id INTEGER PRIMARY KEY AUTOINCREMENT," +
" name TEXT," +
" run_id INTEGER," +
" iteration INTEGER," +
" timestamp TEXT," +
" unknown_delay REAL," +
" input REAL," +
" animation REAL," +
" layout REAL," +
" draw REAL," +
" sync REAL," +
" command_issue REAL," +
" swap_buffers REAL," +
" total_duration REAL," +
" jank_frame BOOLEAN, " +
" device_charging INTEGER);");
}
public void storeRunResults(String testName, int runId, int iteration,
UiBenchmarkResult result) {
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
try {
String date = DateFormat.getDateTimeInstance().format(new Date());
int jankIndexIndex = 0;
int[] sortedJankIndices = result.getSortedJankFrameIndices();
int totalFrameCount = result.getTotalFrameCount();
for (int frameIdx = 0; frameIdx < totalFrameCount; frameIdx++) {
ContentValues cv = new ContentValues();
cv.put("name", testName);
cv.put("run_id", runId);
cv.put("iteration", iteration);
cv.put("timestamp", date);
cv.put("unknown_delay",
result.getMetricAtIndex(frameIdx, FrameMetrics.UNKNOWN_DELAY_DURATION));
cv.put("input",
result.getMetricAtIndex(frameIdx, FrameMetrics.INPUT_HANDLING_DURATION));
cv.put("animation",
result.getMetricAtIndex(frameIdx, FrameMetrics.ANIMATION_DURATION));
cv.put("layout",
result.getMetricAtIndex(frameIdx, FrameMetrics.LAYOUT_MEASURE_DURATION));
cv.put("draw",
result.getMetricAtIndex(frameIdx, FrameMetrics.DRAW_DURATION));
cv.put("sync",
result.getMetricAtIndex(frameIdx, FrameMetrics.SYNC_DURATION));
cv.put("command_issue",
result.getMetricAtIndex(frameIdx, FrameMetrics.COMMAND_ISSUE_DURATION));
cv.put("swap_buffers",
result.getMetricAtIndex(frameIdx, FrameMetrics.SWAP_BUFFERS_DURATION));
cv.put("total_duration",
result.getMetricAtIndex(frameIdx, FrameMetrics.TOTAL_DURATION));
if (jankIndexIndex < sortedJankIndices.length &&
sortedJankIndices[jankIndexIndex] == frameIdx) {
jankIndexIndex++;
cv.put("jank_frame", true);
} else {
cv.put("jank_frame", false);
}
db.insert(UI_RESULTS_TABLE, null, cv);
}
db.setTransactionSuccessful();
Toast.makeText(mContext, "Score: " + result.getScore()
+ " Jank: " + (100 * sortedJankIndices.length) / (float) totalFrameCount + "%",
Toast.LENGTH_LONG).show();
} finally {
db.endTransaction();
}
}
public ArrayList<UiBenchmarkResult> loadTestResults(String testName, int runId) {
SQLiteDatabase db = getReadableDatabase();
ArrayList<UiBenchmarkResult> resultList = new ArrayList<>();
try {
String[] columnsToQuery = new String[] {
"name",
"run_id",
"iteration",
"unknown_delay",
"input",
"animation",
"layout",
"draw",
"sync",
"command_issue",
"swap_buffers",
"total_duration",
};
Cursor cursor = db.query(
UI_RESULTS_TABLE, columnsToQuery, "run_id=? AND name=?",
new String[] { Integer.toString(runId), testName }, null, null, "iteration");
double[] values = new double[columnsToQuery.length - 3];
while (cursor.moveToNext()) {
int iteration = cursor.getInt(cursor.getColumnIndexOrThrow("iteration"));
values[0] = cursor.getDouble(
cursor.getColumnIndexOrThrow("unknown_delay"));
values[1] = cursor.getDouble(
cursor.getColumnIndexOrThrow("input"));
values[2] = cursor.getDouble(
cursor.getColumnIndexOrThrow("animation"));
values[3] = cursor.getDouble(
cursor.getColumnIndexOrThrow("layout"));
values[4] = cursor.getDouble(
cursor.getColumnIndexOrThrow("draw"));
values[5] = cursor.getDouble(
cursor.getColumnIndexOrThrow("sync"));
values[6] = cursor.getDouble(
cursor.getColumnIndexOrThrow("command_issue"));
values[7] = cursor.getDouble(
cursor.getColumnIndexOrThrow("swap_buffers"));
values[8] = cursor.getDouble(
cursor.getColumnIndexOrThrow("total_duration"));
UiBenchmarkResult iterationResult;
if (resultList.size() == iteration) {
iterationResult = new UiBenchmarkResult(values);
resultList.add(iteration, iterationResult);
} else {
iterationResult = resultList.get(iteration);
iterationResult.update(values);
}
}
cursor.close();
} finally {
db.close();
}
int total = resultList.get(0).getTotalFrameCount();
for (int i = 0; i < total; i++) {
System.out.println(""+ resultList.get(0).getMetricAtIndex(0, FrameMetrics.TOTAL_DURATION));
}
return resultList;
}
public HashMap<String, ArrayList<UiBenchmarkResult>> loadDetailedResults(int runId) {
SQLiteDatabase db = getReadableDatabase();
HashMap<String, ArrayList<UiBenchmarkResult>> results = new HashMap<>();
try {
String[] columnsToQuery = new String[] {
"name",
"run_id",
"iteration",
"unknown_delay",
"input",
"animation",
"layout",
"draw",
"sync",
"command_issue",
"swap_buffers",
"total_duration",
};
Cursor cursor = db.query(
UI_RESULTS_TABLE, columnsToQuery, "run_id=?",
new String[] { Integer.toString(runId) }, null, null, "name, iteration");
double[] values = new double[columnsToQuery.length - 3];
while (cursor.moveToNext()) {
int iteration = cursor.getInt(cursor.getColumnIndexOrThrow("iteration"));
String name = cursor.getString(cursor.getColumnIndexOrThrow("name"));
ArrayList<UiBenchmarkResult> resultList = results.get(name);
if (resultList == null) {
resultList = new ArrayList<>();
results.put(name, resultList);
}
values[0] = cursor.getDouble(
cursor.getColumnIndexOrThrow("unknown_delay"));
values[1] = cursor.getDouble(
cursor.getColumnIndexOrThrow("input"));
values[2] = cursor.getDouble(
cursor.getColumnIndexOrThrow("animation"));
values[3] = cursor.getDouble(
cursor.getColumnIndexOrThrow("layout"));
values[4] = cursor.getDouble(
cursor.getColumnIndexOrThrow("draw"));
values[5] = cursor.getDouble(
cursor.getColumnIndexOrThrow("sync"));
values[6] = cursor.getDouble(
cursor.getColumnIndexOrThrow("command_issue"));
values[7] = cursor.getDouble(
cursor.getColumnIndexOrThrow("swap_buffers"));
values[8] = cursor.getDouble(
cursor.getColumnIndexOrThrow("total_duration"));
values[8] = cursor.getDouble(
cursor.getColumnIndexOrThrow("total_duration"));
UiBenchmarkResult iterationResult;
if (resultList.size() == iteration) {
iterationResult = new UiBenchmarkResult(values);
resultList.add(iterationResult);
} else {
iterationResult = resultList.get(iteration);
iterationResult.update(values);
}
}
cursor.close();
} finally {
db.close();
}
return results;
}
public void exportToCsv() throws IOException {
String path = mContext.getFilesDir() + "/results-" + System.currentTimeMillis() + ".csv";
SQLiteDatabase db = getReadableDatabase();
// stats across metrics for each run and each test
HashMap<String, DescriptiveStatistics> stats = new HashMap<>();
Cursor runIdCursor = db.query(
UI_RESULTS_TABLE, new String[] { "run_id" }, null, null, "run_id", null, null);
while (runIdCursor.moveToNext()) {
int runId = runIdCursor.getInt(runIdCursor.getColumnIndexOrThrow("run_id"));
HashMap<String, ArrayList<UiBenchmarkResult>> detailedResults =
loadDetailedResults(runId);
writeRawResults(runId, detailedResults);
DescriptiveStatistics overall = new DescriptiveStatistics();
try (FileWriter writer = new FileWriter(path, true)) {
writer.write("Run ID, " + runId + "\n");
writer.write("Test, Iteration, Score, Jank Penalty, Consistency Bonus, 95th, " +
"90th\n");
for (String testName : detailedResults.keySet()) {
ArrayList<UiBenchmarkResult> results = detailedResults.get(testName);
DescriptiveStatistics scoreStats = new DescriptiveStatistics();
DescriptiveStatistics jankPenalty = new DescriptiveStatistics();
DescriptiveStatistics consistencyBonus = new DescriptiveStatistics();
for (int i = 0; i < results.size(); i++) {
UiBenchmarkResult result = results.get(i);
int score = result.getScore();
scoreStats.addValue(score);
overall.addValue(score);
jankPenalty.addValue(result.getJankPenalty());
consistencyBonus.addValue(result.getConsistencyBonus());
writer.write(testName);
writer.write(",");
writer.write("" + i);
writer.write(",");
writer.write("" + score);
writer.write(",");
writer.write("" + result.getJankPenalty());
writer.write(",");
writer.write("" + result.getConsistencyBonus());
writer.write(",");
writer.write(Double.toString(
result.getPercentile(FrameMetrics.TOTAL_DURATION, 95)));
writer.write(",");
writer.write(Double.toString(
result.getPercentile(FrameMetrics.TOTAL_DURATION, 90)));
writer.write("\n");
}
writer.write("Score CV," +
(100 * scoreStats.getStandardDeviation()
/ scoreStats.getMean()) + "%\n");
writer.write("Jank Penalty CV, " +
(100 * jankPenalty.getStandardDeviation()
/ jankPenalty.getMean()) + "%\n");
writer.write("Consistency Bonus CV, " +
(100 * consistencyBonus.getStandardDeviation()
/ consistencyBonus.getMean()) + "%\n");
writer.write("\n");
}
writer.write("Overall Score CV," +
(100 * overall.getStandardDeviation() / overall.getMean()) + "%\n");
writer.flush();
}
}
runIdCursor.close();
}
private void writeRawResults(int runId,
HashMap<String, ArrayList<UiBenchmarkResult>> detailedResults) {
StringBuilder path = new StringBuilder();
path.append(mContext.getFilesDir());
path.append("/");
path.append(Integer.toString(runId));
path.append(".csv");
try (FileWriter writer = new FileWriter(path.toString())) {
for (String test : detailedResults.keySet()) {
writer.write("Test, " + test + "\n");
writer.write("iteration, unknown delay, input, animation, layout, draw, sync, " +
"command issue, swap buffers\n");
ArrayList<UiBenchmarkResult> runs = detailedResults.get(test);
for (int i = 0; i < runs.size(); i++) {
UiBenchmarkResult run = runs.get(i);
for (int j = 0; j < run.getTotalFrameCount(); j++) {
writer.write(Integer.toString(i) + "," +
run.getMetricAtIndex(j, FrameMetrics.UNKNOWN_DELAY_DURATION) + "," +
run.getMetricAtIndex(j, FrameMetrics.INPUT_HANDLING_DURATION) + "," +
run.getMetricAtIndex(j, FrameMetrics.ANIMATION_DURATION) + "," +
run.getMetricAtIndex(j, FrameMetrics.LAYOUT_MEASURE_DURATION) + "," +
run.getMetricAtIndex(j, FrameMetrics.DRAW_DURATION) + "," +
run.getMetricAtIndex(j, FrameMetrics.SYNC_DURATION) + "," +
run.getMetricAtIndex(j, FrameMetrics.COMMAND_ISSUE_DURATION) + "," +
run.getMetricAtIndex(j, FrameMetrics.SWAP_BUFFERS_DURATION) + "," +
run.getMetricAtIndex(j, FrameMetrics.TOTAL_DURATION) + "\n");
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int currentVersion) {
if (oldVersion < VERSION) {
sqLiteDatabase.execSQL("ALTER TABLE "
+ UI_RESULTS_TABLE + " ADD COLUMN timestamp TEXT;");
}
}
}

View File

@@ -0,0 +1,245 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.results;
import android.annotation.TargetApi;
import android.view.FrameMetrics;
import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
import org.apache.commons.math.stat.descriptive.SummaryStatistics;
import java.util.ArrayList;
import java.util.List;
/**
* Utility for storing and analyzing UI benchmark results.
*/
@TargetApi(24)
public class UiBenchmarkResult {
private static final int BASE_SCORE = 100;
private static final int ZERO_SCORE_TOTAL_DURATION_MS = 32;
private static final int JANK_PENALTY_THRESHOLD_MS = 12;
private static final int ZERO_SCORE_ABOVE_THRESHOLD_MS =
ZERO_SCORE_TOTAL_DURATION_MS - JANK_PENALTY_THRESHOLD_MS;
private static final double JANK_PENALTY_PER_MS_ABOVE_THRESHOLD =
BASE_SCORE / (double)ZERO_SCORE_ABOVE_THRESHOLD_MS;
private static final int CONSISTENCY_BONUS_MAX = 100;
private static final int METRIC_WAS_JANKY = -1;
private static final int[] METRICS = new int[] {
FrameMetrics.UNKNOWN_DELAY_DURATION,
FrameMetrics.INPUT_HANDLING_DURATION,
FrameMetrics.ANIMATION_DURATION,
FrameMetrics.LAYOUT_MEASURE_DURATION,
FrameMetrics.DRAW_DURATION,
FrameMetrics.SYNC_DURATION,
FrameMetrics.COMMAND_ISSUE_DURATION,
FrameMetrics.SWAP_BUFFERS_DURATION,
FrameMetrics.TOTAL_DURATION,
};
public static final int FRAME_PERIOD_MS = 16;
private final DescriptiveStatistics[] mStoredStatistics;
public UiBenchmarkResult(List<FrameMetrics> instances) {
mStoredStatistics = new DescriptiveStatistics[METRICS.length];
insertMetrics(instances);
}
public UiBenchmarkResult(double[] values) {
mStoredStatistics = new DescriptiveStatistics[METRICS.length];
insertValues(values);
}
public void update(List<FrameMetrics> instances) {
insertMetrics(instances);
}
public void update(double[] values) {
insertValues(values);
}
public double getAverage(int id) {
int pos = getMetricPosition(id);
return mStoredStatistics[pos].getMean();
}
public double getMinimum(int id) {
int pos = getMetricPosition(id);
return mStoredStatistics[pos].getMin();
}
public double getMaximum(int id) {
int pos = getMetricPosition(id);
return mStoredStatistics[pos].getMax();
}
public int getMaximumIndex(int id) {
int pos = getMetricPosition(id);
double[] storedMetrics = mStoredStatistics[pos].getValues();
int maxIdx = 0;
for (int i = 0; i < storedMetrics.length; i++) {
if (storedMetrics[i] >= storedMetrics[maxIdx]) {
maxIdx = i;
}
}
return maxIdx;
}
public double getMetricAtIndex(int index, int metricId) {
return mStoredStatistics[getMetricPosition(metricId)].getElement(index);
}
public double getPercentile(int id, int percentile) {
if (percentile > 100) percentile = 100;
if (percentile < 0) percentile = 0;
int metricPos = getMetricPosition(id);
return mStoredStatistics[metricPos].getPercentile(percentile);
}
public int getTotalFrameCount() {
if (mStoredStatistics.length == 0) {
return 0;
}
return (int) mStoredStatistics[0].getN();
}
public int getScore() {
SummaryStatistics badFramesStats = new SummaryStatistics();
int totalFrameCount = getTotalFrameCount();
for (int i = 0; i < totalFrameCount; i++) {
double totalDuration = getMetricAtIndex(i, FrameMetrics.TOTAL_DURATION);
if (totalDuration >= 12) {
badFramesStats.addValue(totalDuration);
}
}
int length = getSortedJankFrameIndices().length;
double jankFrameCount = 100 * length / (double) totalFrameCount;
System.out.println("Mean: " + badFramesStats.getMean() + " JankP: " + jankFrameCount
+ " StdDev: " + badFramesStats.getStandardDeviation() +
" Count Bad: " + badFramesStats.getN() + " Count Jank: " + length);
return (int) Math.round(
(badFramesStats.getMean()) * jankFrameCount * badFramesStats.getStandardDeviation());
}
public int getJankPenalty() {
double total95th = mStoredStatistics[getMetricPosition(FrameMetrics.TOTAL_DURATION)]
.getPercentile(95);
System.out.println("95: " + total95th);
double aboveThreshold = total95th - JANK_PENALTY_THRESHOLD_MS;
if (aboveThreshold <= 0) {
return 0;
}
if (aboveThreshold > ZERO_SCORE_ABOVE_THRESHOLD_MS) {
return BASE_SCORE;
}
return (int) Math.ceil(JANK_PENALTY_PER_MS_ABOVE_THRESHOLD * aboveThreshold);
}
public int getConsistencyBonus() {
DescriptiveStatistics totalDurationStats =
mStoredStatistics[getMetricPosition(FrameMetrics.TOTAL_DURATION)];
double standardDeviation = totalDurationStats.getStandardDeviation();
if (standardDeviation == 0) {
return CONSISTENCY_BONUS_MAX;
}
// 1 / CV of the total duration.
double bonus = totalDurationStats.getMean() / standardDeviation;
return (int) Math.min(Math.round(bonus), CONSISTENCY_BONUS_MAX);
}
public int[] getSortedJankFrameIndices() {
ArrayList<Integer> jankFrameIndices = new ArrayList<>();
boolean tripleBuffered = false;
int totalFrameCount = getTotalFrameCount();
int totalDurationPos = getMetricPosition(FrameMetrics.TOTAL_DURATION);
for (int i = 0; i < totalFrameCount; i++) {
double thisDuration = mStoredStatistics[totalDurationPos].getElement(i);
if (!tripleBuffered) {
if (thisDuration > FRAME_PERIOD_MS) {
tripleBuffered = true;
jankFrameIndices.add(i);
}
} else {
if (thisDuration > 2 * FRAME_PERIOD_MS) {
tripleBuffered = false;
jankFrameIndices.add(i);
}
}
}
int[] res = new int[jankFrameIndices.size()];
int i = 0;
for (Integer index : jankFrameIndices) {
res[i++] = index;
}
return res;
}
private int getMetricPosition(int id) {
for (int i = 0; i < METRICS.length; i++) {
if (id == METRICS[i]) {
return i;
}
}
return -1;
}
private void insertMetrics(List<FrameMetrics> instances) {
for (FrameMetrics frame : instances) {
for (int i = 0; i < METRICS.length; i++) {
DescriptiveStatistics stats = mStoredStatistics[i];
if (stats == null) {
stats = new DescriptiveStatistics();
mStoredStatistics[i] = stats;
}
mStoredStatistics[i].addValue(frame.getMetric(METRICS[i]) / (double) 1000000);
}
}
}
private void insertValues(double[] values) {
if (values.length != METRICS.length) {
throw new IllegalArgumentException("invalid values array");
}
for (int i = 0; i < values.length; i++) {
DescriptiveStatistics stats = mStoredStatistics[i];
if (stats == null) {
stats = new DescriptiveStatistics();
mStoredStatistics[i] = stats;
}
mStoredStatistics[i].addValue(values[i]);
}
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.synthetic;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import com.android.benchmark.R;
import com.android.benchmark.app.PerfTimeline;
import junit.framework.Test;
public class MemoryActivity extends Activity {
private TextView mTextStatus;
private TextView mTextMin;
private TextView mTextMax;
private TextView mTextTypical;
private PerfTimeline mTimeline;
TestInterface mTI;
int mActiveTest;
private class SyntheticTestCallback extends TestInterface.TestResultCallback {
@Override
void onTestResult(int command, float result) {
Intent resultIntent = new Intent();
resultIntent.putExtra("com.android.benchmark.synthetic.TEST_RESULT", result);
setResult(RESULT_OK, resultIntent);
finish();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memory);
mTextStatus = (TextView) findViewById(R.id.textView_status);
mTextMin = (TextView) findViewById(R.id.textView_min);
mTextMax = (TextView) findViewById(R.id.textView_max);
mTextTypical = (TextView) findViewById(R.id.textView_typical);
mTimeline = (PerfTimeline) findViewById(R.id.mem_timeline);
mTI = new TestInterface(mTimeline, 2, new SyntheticTestCallback());
mTI.mTextMax = mTextMax;
mTI.mTextMin = mTextMin;
mTI.mTextStatus = mTextStatus;
mTI.mTextTypical = mTextTypical;
mTimeline.mLinesLow = mTI.mLinesLow;
mTimeline.mLinesHigh = mTI.mLinesHigh;
mTimeline.mLinesValue = mTI.mLinesValue;
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
@Override
protected void onResume() {
super.onResume();
Intent i = getIntent();
mActiveTest = i.getIntExtra("test", 0);
switch (mActiveTest) {
case 0:
mTI.runMemoryBandwidth();
break;
case 1:
mTI.runMemoryLatency();
break;
case 2:
mTI.runPowerManagement();
break;
case 3:
mTI.runCPUHeatSoak();
break;
case 4:
mTI.runCPUGFlops();
break;
default:
break;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_memory, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void onCpuBandwidth(View v) {
}
}

View File

@@ -0,0 +1,451 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.synthetic;
import android.view.View;
import android.widget.TextView;
import org.apache.commons.math.stat.StatUtils;
import org.apache.commons.math.stat.descriptive.SummaryStatistics;
import java.util.LinkedList;
import java.util.Queue;
public class TestInterface {
native long nInit(long options);
native long nDestroy(long b);
native float nGetData(long b, float[] data);
native boolean nRunPowerManagementTest(long b, long options);
native boolean nRunCPUHeatSoakTest(long b, long options);
native boolean nMemTestStart(long b);
native float nMemTestBandwidth(long b, long size);
native float nMemTestLatency(long b, long size);
native void nMemTestEnd(long b);
native float nGFlopsTest(long b, long opt);
public static class TestResultCallback {
void onTestResult(int command, float result) { }
}
static {
System.loadLibrary("nativebench");
}
float[] mLinesLow;
float[] mLinesHigh;
float[] mLinesValue;
TextView mTextStatus;
TextView mTextMin;
TextView mTextMax;
TextView mTextTypical;
private View mViewToUpdate;
private LooperThread mLT;
TestInterface(View v, int runtimeSeconds, TestResultCallback callback) {
int buckets = runtimeSeconds * 1000;
mLinesLow = new float[buckets * 4];
mLinesHigh = new float[buckets * 4];
mLinesValue = new float[buckets * 4];
mViewToUpdate = v;
mLT = new LooperThread(this, callback);
mLT.start();
}
static class LooperThread extends Thread {
public static final int CommandExit = 1;
public static final int TestPowerManagement = 2;
public static final int TestMemoryBandwidth = 3;
public static final int TestMemoryLatency = 4;
public static final int TestHeatSoak = 5;
public static final int TestGFlops = 6;
private volatile boolean mRun = true;
private TestInterface mTI;
private TestResultCallback mCallback;
Queue<Integer> mCommandQueue = new LinkedList<Integer>();
LooperThread(TestInterface ti, TestResultCallback callback) {
super("BenchmarkTestThread");
mTI = ti;
mCallback = callback;
}
void runCommand(int command) {
Integer i = Integer.valueOf(command);
synchronized (this) {
mCommandQueue.add(i);
notifyAll();
}
}
public void run() {
long b = mTI.nInit(0);
if (b == 0) {
return;
}
while (mRun) {
int command = 0;
synchronized (this) {
if (mCommandQueue.isEmpty()) {
try {
wait();
} catch (InterruptedException e) {
}
}
if (!mCommandQueue.isEmpty()) {
command = mCommandQueue.remove();
}
}
switch (command) {
case CommandExit:
mRun = false;
break;
case TestPowerManagement:
float score = mTI.testPowerManagement(b);
mCallback.onTestResult(command, 0);
break;
case TestMemoryBandwidth:
mTI.testCPUMemoryBandwidth(b);
break;
case TestMemoryLatency:
mTI.testCPUMemoryLatency(b);
break;
case TestHeatSoak:
mTI.testCPUHeatSoak(b);
break;
case TestGFlops:
mTI.testCPUGFlops(b);
break;
}
//mViewToUpdate.post(new Runnable() {
// public void run() {
// mViewToUpdate.invalidate();
//}
//});
}
mTI.nDestroy(b);
}
void exit() {
mRun = false;
}
}
void postTextToView(TextView v, String s) {
final TextView tv = v;
final String ts = s;
v.post(new Runnable() {
public void run() {
tv.setText(ts);
}
});
}
float calcAverage(float[] data) {
float total = 0.f;
for (int ct=0; ct < data.length; ct++) {
total += data[ct];
}
return total / data.length;
}
void makeGraph(float[] data, float[] lines) {
for (int ct = 0; ct < data.length; ct++) {
lines[ct * 4 + 0] = (float)ct;
lines[ct * 4 + 1] = 500.f - data[ct];
lines[ct * 4 + 2] = (float)ct;
lines[ct * 4 + 3] = 500.f;
}
}
float testPowerManagement(long b) {
float[] dat = new float[mLinesLow.length / 4];
postTextToView(mTextStatus, "Running single-threaded");
nRunPowerManagementTest(b, 1);
nGetData(b, dat);
makeGraph(dat, mLinesLow);
mViewToUpdate.postInvalidate();
float avgMin = calcAverage(dat);
postTextToView(mTextMin, "Single threaded " + avgMin + " per second");
postTextToView(mTextStatus, "Running multi-threaded");
nRunPowerManagementTest(b, 4);
nGetData(b, dat);
makeGraph(dat, mLinesHigh);
mViewToUpdate.postInvalidate();
float avgMax = calcAverage(dat);
postTextToView(mTextMax, "Multi threaded " + avgMax + " per second");
postTextToView(mTextStatus, "Running typical");
nRunPowerManagementTest(b, 0);
nGetData(b, dat);
makeGraph(dat, mLinesValue);
mViewToUpdate.postInvalidate();
float avgTypical = calcAverage(dat);
float ofIdeal = avgTypical / (avgMax + avgMin) * 200.f;
postTextToView(mTextTypical, String.format("Typical mix (50/50) %%%2.0f of ideal", ofIdeal));
return ofIdeal * (avgMax + avgMin);
}
float testCPUHeatSoak(long b) {
float[] dat = new float[1000];
postTextToView(mTextStatus, "Running heat soak test");
for (int t = 0; t < 1000; t++) {
mLinesLow[t * 4 + 0] = (float)t;
mLinesLow[t * 4 + 1] = 498.f;
mLinesLow[t * 4 + 2] = (float)t;
mLinesLow[t * 4 + 3] = 500.f;
}
float peak = 0.f;
float total = 0.f;
float dThroughput = 0;
float prev = 0;
SummaryStatistics stats = new SummaryStatistics();
for (int t = 0; t < 1000; t++) {
nRunCPUHeatSoakTest(b, 1);
nGetData(b, dat);
float p = calcAverage(dat);
if (prev != 0) {
dThroughput += (prev - p);
}
prev = p;
mLinesLow[t * 4 + 1] = 499.f - p;
if (peak < p) {
peak = p;
}
for (float f : dat) {
stats.addValue(f);
}
total += p;
mViewToUpdate.postInvalidate();
postTextToView(mTextMin, "Peak " + peak + " per second");
postTextToView(mTextMax, "Current " + p + " per second");
postTextToView(mTextTypical, "Average " + (total / (t + 1)) + " per second");
}
float decreaseOverTime = dThroughput / 1000;
System.out.println("dthroughput/dt: " + decreaseOverTime);
float score = (float) (stats.getMean() / (stats.getStandardDeviation() * decreaseOverTime));
postTextToView(mTextStatus, "Score: " + score);
return score;
}
void testCPUMemoryBandwidth(long b) {
int[] sizeK = {1, 2, 3, 4, 5, 6, 7,
8, 10, 12, 14, 16, 20, 24, 28,
32, 40, 48, 56, 64, 80, 96, 112,
128, 160, 192, 224, 256, 320, 384, 448,
512, 640, 768, 896, 1024, 1280, 1536, 1792,
2048, 2560, 3584, 4096, 5120, 6144, 7168,
8192, 10240, 12288, 14336, 16384
};
final int subSteps = 15;
float[] results = new float[sizeK.length * subSteps];
nMemTestStart(b);
float[] dat = new float[1000];
postTextToView(mTextStatus, "Running Memory Bandwidth test");
for (int t = 0; t < 1000; t++) {
mLinesLow[t * 4 + 0] = (float)t;
mLinesLow[t * 4 + 1] = 498.f;
mLinesLow[t * 4 + 2] = (float)t;
mLinesLow[t * 4 + 3] = 500.f;
}
for (int i = 0; i < sizeK.length; i++) {
postTextToView(mTextStatus, "Running " + sizeK[i] + " K");
float rtot = 0.f;
for (int j = 0; j < subSteps; j++) {
float ret = nMemTestBandwidth(b, sizeK[i] * 1024);
rtot += ret;
results[i * subSteps + j] = ret;
mLinesLow[(i * subSteps + j) * 4 + 1] = 499.f - (results[i*15+j] * 20.f);
mViewToUpdate.postInvalidate();
}
rtot /= subSteps;
if (sizeK[i] == 2) {
postTextToView(mTextMin, "2K " + rtot + " GB/s");
}
if (sizeK[i] == 128) {
postTextToView(mTextMax, "128K " + rtot + " GB/s");
}
if (sizeK[i] == 8192) {
postTextToView(mTextTypical, "8M " + rtot + " GB/s");
}
}
nMemTestEnd(b);
postTextToView(mTextStatus, "Done");
}
void testCPUMemoryLatency(long b) {
int[] sizeK = {1, 2, 3, 4, 5, 6, 7,
8, 10, 12, 14, 16, 20, 24, 28,
32, 40, 48, 56, 64, 80, 96, 112,
128, 160, 192, 224, 256, 320, 384, 448,
512, 640, 768, 896, 1024, 1280, 1536, 1792,
2048, 2560, 3584, 4096, 5120, 6144, 7168,
8192, 10240, 12288, 14336, 16384
};
final int subSteps = 15;
float[] results = new float[sizeK.length * subSteps];
nMemTestStart(b);
float[] dat = new float[1000];
postTextToView(mTextStatus, "Running Memory Latency test");
for (int t = 0; t < 1000; t++) {
mLinesLow[t * 4 + 0] = (float)t;
mLinesLow[t * 4 + 1] = 498.f;
mLinesLow[t * 4 + 2] = (float)t;
mLinesLow[t * 4 + 3] = 500.f;
}
for (int i = 0; i < sizeK.length; i++) {
postTextToView(mTextStatus, "Running " + sizeK[i] + " K");
float rtot = 0.f;
for (int j = 0; j < subSteps; j++) {
float ret = nMemTestLatency(b, sizeK[i] * 1024);
rtot += ret;
results[i * subSteps + j] = ret;
if (ret > 400.f) ret = 400.f;
if (ret < 0.f) ret = 0.f;
mLinesLow[(i * subSteps + j) * 4 + 1] = 499.f - ret;
//android.util.Log.e("bench", "test bw " + sizeK[i] + " - " + ret);
mViewToUpdate.postInvalidate();
}
rtot /= subSteps;
if (sizeK[i] == 2) {
postTextToView(mTextMin, "2K " + rtot + " ns");
}
if (sizeK[i] == 128) {
postTextToView(mTextMax, "128K " + rtot + " ns");
}
if (sizeK[i] == 8192) {
postTextToView(mTextTypical, "8M " + rtot + " ns");
}
}
nMemTestEnd(b);
postTextToView(mTextStatus, "Done");
}
void testCPUGFlops(long b) {
int[] sizeK = {1, 2, 3, 4, 5, 6, 7
};
final int subSteps = 15;
float[] results = new float[sizeK.length * subSteps];
nMemTestStart(b);
float[] dat = new float[1000];
postTextToView(mTextStatus, "Running Memory Latency test");
for (int t = 0; t < 1000; t++) {
mLinesLow[t * 4 + 0] = (float)t;
mLinesLow[t * 4 + 1] = 498.f;
mLinesLow[t * 4 + 2] = (float)t;
mLinesLow[t * 4 + 3] = 500.f;
}
for (int i = 0; i < sizeK.length; i++) {
postTextToView(mTextStatus, "Running " + sizeK[i] + " K");
float rtot = 0.f;
for (int j = 0; j < subSteps; j++) {
float ret = nGFlopsTest(b, sizeK[i] * 1024);
rtot += ret;
results[i * subSteps + j] = ret;
if (ret > 400.f) ret = 400.f;
if (ret < 0.f) ret = 0.f;
mLinesLow[(i * subSteps + j) * 4 + 1] = 499.f - ret;
mViewToUpdate.postInvalidate();
}
rtot /= subSteps;
if (sizeK[i] == 2) {
postTextToView(mTextMin, "2K " + rtot + " ns");
}
if (sizeK[i] == 128) {
postTextToView(mTextMax, "128K " + rtot + " ns");
}
if (sizeK[i] == 8192) {
postTextToView(mTextTypical, "8M " + rtot + " ns");
}
}
nMemTestEnd(b);
postTextToView(mTextStatus, "Done");
}
public void runPowerManagement() {
mLT.runCommand(mLT.TestPowerManagement);
}
public void runMemoryBandwidth() {
mLT.runCommand(mLT.TestMemoryBandwidth);
}
public void runMemoryLatency() {
mLT.runCommand(mLT.TestMemoryLatency);
}
public void runCPUHeatSoak() {
mLT.runCommand(mLT.TestHeatSoak);
}
public void runCPUGFlops() {
mLT.runCommand(mLT.TestGFlops);
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.ui;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import com.android.benchmark.R;
import com.android.benchmark.ui.automation.Automator;
import com.android.benchmark.ui.automation.Interaction;
/**
*
*/
public class BitmapUploadActivity extends AppCompatActivity {
private Automator mAutomator;
public static class UploadView extends View {
private int mColorValue;
private Bitmap mBitmap;
private final DisplayMetrics mMetrics = new DisplayMetrics();
private final Rect mRect = new Rect();
public UploadView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@SuppressWarnings("unused")
public void setColorValue(int colorValue) {
if (colorValue == mColorValue) return;
mColorValue = colorValue;
// modify the bitmap's color to ensure it's uploaded to the GPU
mBitmap.eraseColor(Color.rgb(mColorValue, 255 - mColorValue, 255));
invalidate();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getDisplay().getMetrics(mMetrics);
int minDisplayDimen = Math.min(mMetrics.widthPixels, mMetrics.heightPixels);
int bitmapSize = Math.min((int) (minDisplayDimen * 0.75), 720);
if (mBitmap == null
|| mBitmap.getWidth() != bitmapSize
|| mBitmap.getHeight() != bitmapSize) {
mBitmap = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888);
}
}
@Override
protected void onDraw(Canvas canvas) {
if (mBitmap != null) {
mRect.set(0, 0, getWidth(), getHeight());
canvas.drawBitmap(mBitmap, null, mRect, null);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// animate color to force bitmap uploads
return super.onTouchEvent(event);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bitmap_upload);
final View uploadRoot = findViewById(R.id.upload_root);
uploadRoot.setKeepScreenOn(true);
uploadRoot.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
UploadView uploadView = (UploadView) findViewById(R.id.upload_view);
ObjectAnimator colorValueAnimator =
ObjectAnimator.ofInt(uploadView, "colorValue", 0, 255);
colorValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
colorValueAnimator.setRepeatCount(100);
colorValueAnimator.start();
// animate scene root to guarantee there's a minimum amount of GPU rendering work
ObjectAnimator yAnimator = ObjectAnimator.ofFloat(
view, "translationY", 0, 100);
yAnimator.setRepeatMode(ValueAnimator.REVERSE);
yAnimator.setRepeatCount(100);
yAnimator.start();
return true;
}
});
final UploadView uploadView = (UploadView) findViewById(R.id.upload_view);
final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0);
final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1);
mAutomator = new Automator("BMUpload", runId, iteration, getWindow(),
new Automator.AutomateCallback() {
@Override
public void onPostAutomate() {
Intent result = new Intent();
setResult(RESULT_OK, result);
finish();
}
@Override
public void onAutomate() {
int[] coordinates = new int[2];
uploadRoot.getLocationOnScreen(coordinates);
int x = coordinates[0];
int y = coordinates[1];
float width = uploadRoot.getWidth();
float height = uploadRoot.getHeight();
float middleX = (x + width) / 5;
float middleY = (y + height) / 5;
addInteraction(Interaction.newTap(middleX, middleY));
}
});
mAutomator.start();
}
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.ui;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.KeyEvent;
import android.widget.EditText;
import com.android.benchmark.R;
import com.android.benchmark.ui.automation.Automator;
import com.android.benchmark.ui.automation.Interaction;
public class EditTextInputActivity extends AppCompatActivity {
private Automator mAutomator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final EditText editText = new EditText(this);
final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0);
final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1);
editText.setWidth(400);
editText.setHeight(200);
setContentView(editText);
String testName = getString(R.string.edit_text_input_name);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(testName);
}
mAutomator = new Automator(testName, runId, iteration, getWindow(),
new Automator.AutomateCallback() {
@Override
public void onPostAutomate() {
Intent result = new Intent();
setResult(RESULT_OK, result);
finish();
}
@Override
public void onAutomate() {
int[] coordinates = new int[2];
editText.getLocationOnScreen(coordinates);
int x = coordinates[0];
int y = coordinates[1];
float width = editText.getWidth();
float height = editText.getHeight();
float middleX = (x + width) / 2;
float middleY = (y + height) / 2;
Interaction tap = Interaction.newTap(middleX, middleY);
addInteraction(tap);
int[] alphabet = {
KeyEvent.KEYCODE_A,
KeyEvent.KEYCODE_B,
KeyEvent.KEYCODE_C,
KeyEvent.KEYCODE_D,
KeyEvent.KEYCODE_E,
KeyEvent.KEYCODE_F,
KeyEvent.KEYCODE_G,
KeyEvent.KEYCODE_H,
KeyEvent.KEYCODE_I,
KeyEvent.KEYCODE_J,
KeyEvent.KEYCODE_K,
KeyEvent.KEYCODE_L,
KeyEvent.KEYCODE_M,
KeyEvent.KEYCODE_N,
KeyEvent.KEYCODE_O,
KeyEvent.KEYCODE_P,
KeyEvent.KEYCODE_Q,
KeyEvent.KEYCODE_R,
KeyEvent.KEYCODE_S,
KeyEvent.KEYCODE_T,
KeyEvent.KEYCODE_U,
KeyEvent.KEYCODE_V,
KeyEvent.KEYCODE_W,
KeyEvent.KEYCODE_X,
KeyEvent.KEYCODE_Y,
KeyEvent.KEYCODE_Z,
KeyEvent.KEYCODE_SPACE
};
Interaction typeAlphabet = Interaction.newKeyInput(new int[] {
KeyEvent.KEYCODE_A,
KeyEvent.KEYCODE_B,
KeyEvent.KEYCODE_C,
KeyEvent.KEYCODE_D,
KeyEvent.KEYCODE_E,
KeyEvent.KEYCODE_F,
KeyEvent.KEYCODE_G,
KeyEvent.KEYCODE_H,
KeyEvent.KEYCODE_I,
KeyEvent.KEYCODE_J,
KeyEvent.KEYCODE_K,
KeyEvent.KEYCODE_L,
KeyEvent.KEYCODE_M,
KeyEvent.KEYCODE_N,
KeyEvent.KEYCODE_O,
KeyEvent.KEYCODE_P,
KeyEvent.KEYCODE_Q,
KeyEvent.KEYCODE_R,
KeyEvent.KEYCODE_S,
KeyEvent.KEYCODE_T,
KeyEvent.KEYCODE_U,
KeyEvent.KEYCODE_V,
KeyEvent.KEYCODE_W,
KeyEvent.KEYCODE_X,
KeyEvent.KEYCODE_Y,
KeyEvent.KEYCODE_Z,
KeyEvent.KEYCODE_SPACE,
});
for (int i = 0; i < 5; i++) {
addInteraction(typeAlphabet);
}
}
});
mAutomator.start();
}
@Override
protected void onPause() {
super.onPause();
if (mAutomator != null) {
mAutomator.cancel();
mAutomator = null;
}
}
private String getRunFilename() {
StringBuilder builder = new StringBuilder();
builder.append(getClass().getSimpleName());
builder.append(System.currentTimeMillis());
return builder.toString();
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.ui;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import com.android.benchmark.R;
import com.android.benchmark.registry.BenchmarkRegistry;
import com.android.benchmark.ui.automation.Automator;
import com.android.benchmark.ui.automation.Interaction;
public class FullScreenOverdrawActivity extends AppCompatActivity {
private Automator mAutomator;
private class OverdrawView extends View {
Paint paint = new Paint();
int mColorValue = 0;
public OverdrawView(Context context) {
super(context);
}
@SuppressWarnings("unused")
public void setColorValue(int colorValue) {
mColorValue = colorValue;
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(this, "colorValue", 0, 255);
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
objectAnimator.setRepeatCount(100);
objectAnimator.start();
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
paint.setColor(Color.rgb(mColorValue, 255 - mColorValue, 255));
for (int i = 0; i < 10; i++) {
canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final OverdrawView overdrawView = new OverdrawView(this);
overdrawView.setKeepScreenOn(true);
setContentView(overdrawView);
final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0);
final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1);
String name = BenchmarkRegistry.getBenchmarkName(this, R.id.benchmark_overdraw);
mAutomator = new Automator(name, runId, iteration, getWindow(),
new Automator.AutomateCallback() {
@Override
public void onPostAutomate() {
Intent result = new Intent();
setResult(RESULT_OK, result);
finish();
}
@Override
public void onAutomate() {
int[] coordinates = new int[2];
overdrawView.getLocationOnScreen(coordinates);
int x = coordinates[0];
int y = coordinates[1];
float width = overdrawView.getWidth();
float height = overdrawView.getHeight();
float middleX = (x + width) / 5;
float middleY = (y + height) / 5;
addInteraction(Interaction.newTap(middleX, middleY));
}
});
mAutomator.start();
}
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.ui;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.TextView;
import com.android.benchmark.R;
import java.lang.ref.WeakReference;
import java.util.HashMap;
public class ImageListViewScrollActivity extends ListViewScrollActivity {
private static final int LIST_SIZE = 100;
private static final int[] IMG_RES_ID = new int[]{
R.drawable.img1,
R.drawable.img2,
R.drawable.img3,
R.drawable.img4,
R.drawable.img1,
R.drawable.img2,
R.drawable.img3,
R.drawable.img4,
R.drawable.img1,
R.drawable.img2,
R.drawable.img3,
R.drawable.img4,
R.drawable.img1,
R.drawable.img2,
R.drawable.img3,
R.drawable.img4,
};
private static Bitmap[] mBitmapCache = new Bitmap[IMG_RES_ID.length];
private static final String[] WORDS = Utils.buildStringList(LIST_SIZE);
private HashMap<View, BitmapWorkerTask> mInFlight = new HashMap<>();
@Override
protected ListAdapter createListAdapter() {
return new ImageListAdapter();
}
@Override
protected String getName() {
return getString(R.string.image_list_view_scroll_name);
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
private int cacheIdx = 0;
volatile boolean cancelled = false;
public BitmapWorkerTask(ImageView imageView, int cacheIdx) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<>(imageView);
this.cacheIdx = cacheIdx;
}
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
return Utils.decodeSampledBitmapFromResource(getResources(), data, 100, 100);
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
if (!cancelled) {
imageView.setImageBitmap(bitmap);
}
mBitmapCache[cacheIdx] = bitmap;
}
}
}
}
@Override
protected void onPause() {
super.onPause();
for (int i = 0; i < mBitmapCache.length; i++) {
mBitmapCache[i] = null;
}
}
class ImageListAdapter extends BaseAdapter {
@Override
public int getCount() {
return LIST_SIZE;
}
@Override
public Object getItem(int postition) {
return null;
}
@Override
public long getItemId(int postition) {
return postition;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getBaseContext())
.inflate(R.layout.image_scroll_list_item, parent, false);
}
ImageView imageView = (ImageView) convertView.findViewById(R.id.image_scroll_image);
BitmapWorkerTask inFlight = mInFlight.get(convertView);
if (inFlight != null) {
inFlight.cancelled = true;
mInFlight.remove(convertView);
}
int cacheIdx = position % IMG_RES_ID.length;
Bitmap bitmap = mBitmapCache[(cacheIdx)];
if (bitmap == null) {
BitmapWorkerTask bitmapWorkerTask = new BitmapWorkerTask(imageView, cacheIdx);
bitmapWorkerTask.execute(IMG_RES_ID[(cacheIdx)]);
mInFlight.put(convertView, bitmapWorkerTask);
}
imageView.setImageBitmap(bitmap);
TextView textView = (TextView) convertView.findViewById(R.id.image_scroll_text);
textView.setText(WORDS[position]);
return convertView;
}
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.ui;
import android.app.ActionBar;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.ListFragment;
import android.support.v7.app.AppCompatActivity;
import android.view.Window;
import android.widget.ListAdapter;
import com.android.benchmark.R;
/**
* Simple list activity base class
*/
public abstract class ListActivityBase extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_fragment);
ActionBar actionBar = getActionBar();
if (actionBar != null) {
actionBar.setTitle(getName());
}
if (findViewById(R.id.list_fragment_container) != null) {
FragmentManager fm = getSupportFragmentManager();
ListFragment listView = new ListFragment();
listView.setListAdapter(createListAdapter());
fm.beginTransaction().add(R.id.list_fragment_container, listView).commit();
}
}
protected abstract ListAdapter createListAdapter();
protected abstract String getName();
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.ui;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.FrameMetrics;
import android.view.MotionEvent;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
import com.android.benchmark.R;
import com.android.benchmark.ui.automation.Automator;
import com.android.benchmark.ui.automation.Interaction;
import java.io.File;
import java.util.List;
public class ListViewScrollActivity extends ListActivityBase {
private static final int LIST_SIZE = 400;
private static final int INTERACTION_COUNT = 4;
private Automator mAutomator;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0);
final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(getTitle());
}
mAutomator = new Automator(getName(), runId, iteration, getWindow(),
new Automator.AutomateCallback() {
@Override
public void onPostAutomate() {
Intent result = new Intent();
setResult(RESULT_OK, result);
finish();
}
@Override
public void onPostInteraction(List<FrameMetrics> metrics) {}
@Override
public void onAutomate() {
FrameLayout v = (FrameLayout) findViewById(R.id.list_fragment_container);
int[] coordinates = new int[2];
v.getLocationOnScreen(coordinates);
int x = coordinates[0];
int y = coordinates[1];
float width = v.getWidth();
float height = v.getHeight();
float middleX = (x + width) / 5;
float middleY = (y + height) / 5;
Interaction flingUp = Interaction.newFlingUp(middleX, middleY);
Interaction flingDown = Interaction.newFlingDown(middleX, middleY);
for (int i = 0; i < INTERACTION_COUNT; i++) {
addInteraction(flingUp);
addInteraction(flingDown);
}
}
});
mAutomator.start();
}
@Override
protected void onPause() {
super.onPause();
if (mAutomator != null) {
mAutomator.cancel();
mAutomator = null;
}
}
@Override
protected ListAdapter createListAdapter() {
return new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
Utils.buildStringList(LIST_SIZE));
}
@Override
protected String getName() {
return getString(R.string.list_view_scroll_name);
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.ui;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.ListFragment;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.android.benchmark.R;
import com.android.benchmark.ui.automation.Automator;
import com.android.benchmark.ui.automation.Interaction;
public class ShadowGridActivity extends AppCompatActivity {
private Automator mAutomator;
public static class MyListFragment extends ListFragment {
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getListView().setDivider(null);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0);
final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1);
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentById(android.R.id.content) == null) {
ListFragment listFragment = new MyListFragment();
listFragment.setListAdapter(new ArrayAdapter<>(this,
R.layout.card_row, R.id.card_text, Utils.buildStringList(200)));
fm.beginTransaction().add(android.R.id.content, listFragment).commit();
String testName = getString(R.string.shadow_grid_name);
mAutomator = new Automator(testName, runId, iteration, getWindow(),
new Automator.AutomateCallback() {
@Override
public void onPostAutomate() {
Intent result = new Intent();
setResult(RESULT_OK, result);
finish();
}
@Override
public void onAutomate() {
ListView v = (ListView) findViewById(android.R.id.list);
int[] coordinates = new int[2];
v.getLocationOnScreen(coordinates);
int x = coordinates[0];
int y = coordinates[1];
float width = v.getWidth();
float height = v.getHeight();
float middleX = (x + width) / 2;
float middleY = (y + height) / 2;
Interaction flingUp = Interaction.newFlingUp(middleX, middleY);
Interaction flingDown = Interaction.newFlingDown(middleX, middleY);
addInteraction(flingUp);
addInteraction(flingDown);
addInteraction(flingUp);
addInteraction(flingDown);
addInteraction(flingUp);
addInteraction(flingDown);
addInteraction(flingUp);
addInteraction(flingDown);
}
});
mAutomator.start();
}
}
@Override
protected void onPause() {
super.onPause();
if (mAutomator != null) {
mAutomator.cancel();
mAutomator = null;
}
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.ui;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import com.android.benchmark.registry.BenchmarkRegistry;
import com.android.benchmark.ui.automation.Automator;
import com.android.benchmark.ui.automation.Interaction;
import java.io.File;
public class TextScrollActivity extends ListActivityBase {
public static final String EXTRA_HIT_RATE = ".TextScrollActivity.EXTRA_HIT_RATE";
private static final int PARAGRAPH_COUNT = 200;
private int mHitPercentage = 100;
private Automator mAutomator;
private String mName;
@Override
public void onCreate(Bundle savedInstanceState) {
mHitPercentage = getIntent().getIntExtra(EXTRA_HIT_RATE,
mHitPercentage);
super.onCreate(savedInstanceState);
final int runId = getIntent().getIntExtra("com.android.benchmark.RUN_ID", 0);
final int iteration = getIntent().getIntExtra("com.android.benchmark.ITERATION", -1);
final int id = getIntent().getIntExtra(BenchmarkRegistry.EXTRA_ID, -1);
if (id == -1) {
finish();
return;
}
mName = BenchmarkRegistry.getBenchmarkName(this, id);
mAutomator = new Automator(getName(), runId, iteration, getWindow(),
new Automator.AutomateCallback() {
@Override
public void onPostAutomate() {
Intent result = new Intent();
setResult(RESULT_OK, result);
finish();
}
@Override
public void onAutomate() {
ListView v = (ListView) findViewById(android.R.id.list);
int[] coordinates = new int[2];
v.getLocationOnScreen(coordinates);
int x = coordinates[0];
int y = coordinates[1];
float width = v.getWidth();
float height = v.getHeight();
float middleX = (x + width) / 2;
float middleY = (y + height) / 2;
Interaction flingUp = Interaction.newFlingUp(middleX, middleY);
Interaction flingDown = Interaction.newFlingDown(middleX, middleY);
addInteraction(flingUp);
addInteraction(flingDown);
addInteraction(flingUp);
addInteraction(flingDown);
addInteraction(flingUp);
addInteraction(flingDown);
addInteraction(flingUp);
addInteraction(flingDown);
}
});
mAutomator.start();
}
@Override
protected void onPause() {
super.onPause();
if (mAutomator != null) {
mAutomator.cancel();
mAutomator = null;
}
}
@Override
protected ListAdapter createListAdapter() {
return new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
Utils.buildParagraphListWithHitPercentage(PARAGRAPH_COUNT, 80));
}
@Override
protected String getName() {
return mName;
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.ui;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import java.util.Random;
public class Utils {
private static final int RANDOM_WORD_LENGTH = 10;
public static String getRandomWord(Random random, int length) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
char base = random.nextBoolean() ? 'A' : 'a';
char nextChar = (char)(random.nextInt(26) + base);
builder.append(nextChar);
}
return builder.toString();
}
public static String[] buildStringList(int count) {
Random random = new Random(0);
String[] result = new String[count];
for (int i = 0; i < count; i++) {
result[i] = getRandomWord(random, RANDOM_WORD_LENGTH);
}
return result;
}
// a small number of strings reused frequently, expected to hit
// in the word-granularity text layout cache
static final String[] CACHE_HIT_STRINGS = new String[] {
"a",
"small",
"number",
"of",
"strings",
"reused",
"frequently"
};
private static final int WORDS_IN_PARAGRAPH = 150;
// misses are fairly long 'words' to ensure they miss
private static final int PARAGRAPH_MISS_MIN_LENGTH = 4;
private static final int PARAGRAPH_MISS_MAX_LENGTH = 9;
static String[] buildParagraphListWithHitPercentage(int paragraphCount, int hitPercentage) {
if (hitPercentage < 0 || hitPercentage > 100) throw new IllegalArgumentException();
String[] strings = new String[paragraphCount];
Random random = new Random(0);
for (int i = 0; i < strings.length; i++) {
StringBuilder result = new StringBuilder();
for (int word = 0; word < WORDS_IN_PARAGRAPH; word++) {
if (word != 0) {
result.append(" ");
}
if (random.nextInt(100) < hitPercentage) {
// add a common word, which is very likely to hit in the cache
result.append(CACHE_HIT_STRINGS[random.nextInt(CACHE_HIT_STRINGS.length)]);
} else {
// construct a random word, which will *most likely* miss
int length = PARAGRAPH_MISS_MIN_LENGTH;
length += random.nextInt(PARAGRAPH_MISS_MAX_LENGTH - PARAGRAPH_MISS_MIN_LENGTH);
result.append(getRandomWord(random, length));
}
}
strings[i] = result.toString();
}
return strings;
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.ui.automation;
import android.annotation.TargetApi;
import android.app.Instrumentation;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.view.FrameMetrics;
import android.view.MotionEvent;
import android.view.ViewTreeObserver;
import android.view.Window;
import com.android.benchmark.results.GlobalResultsStore;
import com.android.benchmark.results.UiBenchmarkResult;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@TargetApi(24)
public class Automator extends HandlerThread
implements ViewTreeObserver.OnGlobalLayoutListener, CollectorThread.CollectorListener {
public static final long FRAME_PERIOD_MILLIS = 16;
private static final int PRE_READY_STATE_COUNT = 3;
private static final String TAG = "Benchmark.Automator";
private final AtomicInteger mReadyState;
private AutomateCallback mCallback;
private Window mWindow;
private AutomatorHandler mHandler;
private CollectorThread mCollectorThread;
private int mRunId;
private int mIteration;
private String mTestName;
public static class AutomateCallback {
public void onAutomate() {}
public void onPostInteraction(List<FrameMetrics> metrics) {}
public void onPostAutomate() {}
protected final void addInteraction(Interaction interaction) {
if (mInteractions == null) {
return;
}
mInteractions.add(interaction);
}
protected final void setInteractions(List<Interaction> interactions) {
mInteractions = interactions;
}
private List<Interaction> mInteractions;
}
private static final class AutomatorHandler extends Handler {
public static final int MSG_NEXT_INTERACTION = 0;
public static final int MSG_ON_AUTOMATE = 1;
public static final int MSG_ON_POST_INTERACTION = 2;
private final String mTestName;
private final int mRunId;
private final int mIteration;
private Instrumentation mInstrumentation;
private volatile boolean mCancelled;
private CollectorThread mCollectorThread;
private AutomateCallback mCallback;
private Window mWindow;
LinkedList<Interaction> mInteractions;
private UiBenchmarkResult mResults;
AutomatorHandler(Looper looper, Window window, CollectorThread collectorThread,
AutomateCallback callback, String testName, int runId, int iteration) {
super(looper);
mInstrumentation = new Instrumentation();
mCallback = callback;
mWindow = window;
mCollectorThread = collectorThread;
mInteractions = new LinkedList<>();
mTestName = testName;
mRunId = runId;
mIteration = iteration;
}
@Override
public void handleMessage(Message msg) {
if (mCancelled) {
return;
}
switch (msg.what) {
case MSG_NEXT_INTERACTION:
if (!nextInteraction()) {
stopCollector();
writeResults();
mCallback.onPostAutomate();
}
break;
case MSG_ON_AUTOMATE:
mCollectorThread.attachToWindow(mWindow);
mCallback.setInteractions(mInteractions);
mCallback.onAutomate();
postNextInteraction();
break;
case MSG_ON_POST_INTERACTION:
List<FrameMetrics> collectedStats = (List<FrameMetrics>)msg.obj;
persistResults(collectedStats);
mCallback.onPostInteraction(collectedStats);
postNextInteraction();
break;
}
}
public void cancel() {
mCancelled = true;
stopCollector();
}
private void stopCollector() {
mCollectorThread.quitCollector();
}
private boolean nextInteraction() {
Interaction interaction = mInteractions.poll();
if (interaction != null) {
doInteraction(interaction);
return true;
}
return false;
}
private void doInteraction(Interaction interaction) {
if (mCancelled) {
return;
}
mCollectorThread.markInteractionStart();
if (interaction.getType() == Interaction.Type.KEY_EVENT) {
for (int code : interaction.getKeyCodes()) {
if (!mCancelled) {
mInstrumentation.sendKeyDownUpSync(code);
} else {
break;
}
}
} else {
for (MotionEvent event : interaction.getEvents()) {
if (!mCancelled) {
mInstrumentation.sendPointerSync(event);
} else {
break;
}
}
}
}
protected void postNextInteraction() {
final Message msg = obtainMessage(AutomatorHandler.MSG_NEXT_INTERACTION);
sendMessage(msg);
}
private void persistResults(List<FrameMetrics> stats) {
if (stats.isEmpty()) {
return;
}
if (mResults == null) {
mResults = new UiBenchmarkResult(stats);
} else {
mResults.update(stats);
}
}
private void writeResults() {
GlobalResultsStore.getInstance(mWindow.getContext())
.storeRunResults(mTestName, mRunId, mIteration, mResults);
}
}
private void initHandler() {
mHandler = new AutomatorHandler(getLooper(), mWindow, mCollectorThread, mCallback,
mTestName, mRunId, mIteration);
mWindow = null;
mCallback = null;
mCollectorThread = null;
mTestName = null;
mRunId = 0;
mIteration = 0;
}
@Override
public final void onGlobalLayout() {
if (!mCollectorThread.isAlive()) {
mCollectorThread.start();
mWindow.getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
mReadyState.decrementAndGet();
}
}
@Override
public void onCollectorThreadReady() {
if (mReadyState.decrementAndGet() == 0) {
initHandler();
postOnAutomate();
}
}
@Override
protected void onLooperPrepared() {
if (mReadyState.decrementAndGet() == 0) {
initHandler();
postOnAutomate();
}
}
@Override
public void onPostInteraction(List<FrameMetrics> stats) {
Message m = mHandler.obtainMessage(AutomatorHandler.MSG_ON_POST_INTERACTION, stats);
mHandler.sendMessage(m);
}
protected void postOnAutomate() {
final Message msg = mHandler.obtainMessage(AutomatorHandler.MSG_ON_AUTOMATE);
mHandler.sendMessage(msg);
}
public void cancel() {
mHandler.removeMessages(AutomatorHandler.MSG_NEXT_INTERACTION);
mHandler.cancel();
mHandler = null;
}
public Automator(String testName, int runId, int iteration,
Window window, AutomateCallback callback) {
super("AutomatorThread");
mTestName = testName;
mRunId = runId;
mIteration = iteration;
mCallback = callback;
mWindow = window;
mWindow.getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(this);
mCollectorThread = new CollectorThread(this);
mReadyState = new AtomicInteger(PRE_READY_STATE_COUNT);
}
}

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark.ui.automation;
import android.annotation.TargetApi;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;
import android.view.FrameMetrics;
import android.view.Window;
import java.lang.ref.WeakReference;
import java.util.LinkedList;
import java.util.List;
/**
*
*/
final class CollectorThread extends HandlerThread {
private FrameStatsCollector mCollector;
private Window mAttachedWindow;
private List<FrameMetrics> mFrameTimingStats;
private long mLastFrameTime;
private WatchdogHandler mWatchdog;
private WeakReference<CollectorListener> mListener;
private volatile boolean mCollecting;
interface CollectorListener {
void onCollectorThreadReady();
void onPostInteraction(List<FrameMetrics> stats);
}
private final class WatchdogHandler extends Handler {
private static final long SCHEDULE_INTERVAL_MILLIS = 20 * Automator.FRAME_PERIOD_MILLIS;
private static final int MSG_SCHEDULE = 0;
@Override
public void handleMessage(Message msg) {
if (!mCollecting) {
return;
}
long currentTime = SystemClock.uptimeMillis();
if (mLastFrameTime + SCHEDULE_INTERVAL_MILLIS <= currentTime) {
// haven't seen a frame in a while, interaction is probably done
mCollecting = false;
CollectorListener listener = mListener.get();
if (listener != null) {
listener.onPostInteraction(mFrameTimingStats);
}
} else {
schedule();
}
}
public void schedule() {
sendMessageDelayed(obtainMessage(MSG_SCHEDULE), SCHEDULE_INTERVAL_MILLIS);
}
public void deschedule() {
removeMessages(MSG_SCHEDULE);
}
}
static boolean tripleBuffered = false;
static int janks = 0;
static int total = 0;
@TargetApi(24)
private class FrameStatsCollector implements Window.OnFrameMetricsAvailableListener {
@Override
public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, int dropCount) {
if (!mCollecting) {
return;
}
mFrameTimingStats.add(new FrameMetrics(frameMetrics));
mLastFrameTime = SystemClock.uptimeMillis();
}
}
public CollectorThread(CollectorListener listener) {
super("FrameStatsCollectorThread");
mFrameTimingStats = new LinkedList<>();
mListener = new WeakReference<>(listener);
}
@TargetApi(24)
public void attachToWindow(Window window) {
if (mAttachedWindow != null) {
mAttachedWindow.removeOnFrameMetricsAvailableListener(mCollector);
}
mAttachedWindow = window;
window.addOnFrameMetricsAvailableListener(mCollector, new Handler(getLooper()));
}
@TargetApi(24)
public synchronized void detachFromWindow() {
if (mAttachedWindow != null) {
mAttachedWindow.removeOnFrameMetricsAvailableListener(mCollector);
}
mAttachedWindow = null;
}
@TargetApi(24)
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
mCollector = new FrameStatsCollector();
mWatchdog = new WatchdogHandler();
CollectorListener listener = mListener.get();
if (listener != null) {
listener.onCollectorThreadReady();
}
}
public boolean quitCollector() {
stopCollecting();
detachFromWindow();
System.out.println("Jank Percentage: " + (100 * janks/ (double) total) + "%");
tripleBuffered = false;
total = 0;
janks = 0;
return quit();
}
void stopCollecting() {
if (!mCollecting) {
return;
}
mCollecting = false;
mWatchdog.deschedule();
}
public void markInteractionStart() {
mLastFrameTime = 0;
mFrameTimingStats.clear();
mCollecting = true;
mWatchdog.schedule();
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.ui.automation;
import android.support.annotation.IntDef;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Arrays;
public class FrameTimingStats {
@IntDef ({
Index.FLAGS,
Index.INTENDED_VSYNC,
Index.VSYNC,
Index.OLDEST_INPUT_EVENT,
Index.NEWEST_INPUT_EVENT,
Index.HANDLE_INPUT_START,
Index.ANIMATION_START,
Index.PERFORM_TRAVERSALS_START,
Index.DRAW_START,
Index.SYNC_QUEUED,
Index.SYNC_START,
Index.ISSUE_DRAW_COMMANDS_START,
Index.SWAP_BUFFERS,
Index.FRAME_COMPLETED,
})
public @interface Index {
int FLAGS = 0;
int INTENDED_VSYNC = 1;
int VSYNC = 2;
int OLDEST_INPUT_EVENT = 3;
int NEWEST_INPUT_EVENT = 4;
int HANDLE_INPUT_START = 5;
int ANIMATION_START = 6;
int PERFORM_TRAVERSALS_START = 7;
int DRAW_START = 8;
int SYNC_QUEUED = 9;
int SYNC_START = 10;
int ISSUE_DRAW_COMMANDS_START = 11;
int SWAP_BUFFERS = 12;
int FRAME_COMPLETED = 13;
int FRAME_STATS_COUNT = 14; // must always be last
}
private final long[] mStats;
FrameTimingStats(long[] stats) {
mStats = Arrays.copyOf(stats, Index.FRAME_STATS_COUNT);
}
public FrameTimingStats(DataInputStream inputStream) throws IOException {
mStats = new long[Index.FRAME_STATS_COUNT];
update(inputStream);
}
public void update(DataInputStream inputStream) throws IOException {
for (int i = 0; i < mStats.length; i++) {
mStats[i] = inputStream.readLong();
}
}
public long get(@Index int index) {
return mStats[index];
}
public long[] data() {
return mStats;
}
}

View File

@@ -0,0 +1,187 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.ui.automation;
import android.os.SystemClock;
import android.support.annotation.IntDef;
import android.view.MotionEvent;
import java.util.ArrayList;
import java.util.List;
/**
* Encodes a UI interaction as a series of MotionEvents
*/
public class Interaction {
private static final int STEP_COUNT = 20;
// TODO: scale to device display density
private static final int DEFAULT_FLING_SIZE_PX = 500;
private static final int DEFAULT_FLING_DURATION_MS = 20;
private static final int DEFAULT_TAP_DURATION_MS = 20;
private List<MotionEvent> mEvents;
// Interaction parameters
private final float[] mXPositions;
private final float[] mYPositions;
private final long mDuration;
private final int[] mKeyCodes;
private final @Interaction.Type int mType;
@IntDef({
Interaction.Type.TAP,
Interaction.Type.FLING,
Interaction.Type.PINCH,
Interaction.Type.KEY_EVENT})
public @interface Type {
int TAP = 0;
int FLING = 1;
int PINCH = 2;
int KEY_EVENT = 3;
}
public static Interaction newFling(float startX, float startY,
float endX, float endY, long duration) {
return new Interaction(Interaction.Type.FLING, new float[]{startX, endX},
new float[]{startY, endY}, duration);
}
public static Interaction newFlingDown(float startX, float startY) {
return new Interaction(Interaction.Type.FLING,
new float[]{startX, startX},
new float[]{startY, startY + DEFAULT_FLING_SIZE_PX}, DEFAULT_FLING_DURATION_MS);
}
public static Interaction newFlingUp(float startX, float startY) {
return new Interaction(Interaction.Type.FLING,
new float[]{startX, startX}, new float[]{startY, startY - DEFAULT_FLING_SIZE_PX},
DEFAULT_FLING_DURATION_MS);
}
public static Interaction newTap(float startX, float startY) {
return new Interaction(Interaction.Type.TAP,
new float[]{startX, startX}, new float[]{startY, startY},
DEFAULT_FLING_DURATION_MS);
}
public static Interaction newKeyInput(int[] keyCodes) {
return new Interaction(keyCodes);
}
public List<MotionEvent> getEvents() {
switch (mType) {
case Type.FLING:
mEvents = createInterpolatedEventList(mXPositions, mYPositions, mDuration);
break;
case Type.PINCH:
break;
case Type.TAP:
mEvents = createInterpolatedEventList(mXPositions, mYPositions, mDuration);
break;
}
return mEvents;
}
public int getType() {
return mType;
}
public int[] getKeyCodes() {
return mKeyCodes;
}
private static List<MotionEvent> createInterpolatedEventList(
float[] xPos, float[] yPos, long duration) {
long startTime = SystemClock.uptimeMillis() + 100;
List<MotionEvent> result = new ArrayList<>();
float startX = xPos[0];
float startY = yPos[0];
MotionEvent downEvent = MotionEvent.obtain(
startTime, startTime, MotionEvent.ACTION_DOWN, startX, startY, 0);
result.add(downEvent);
for (int i = 1; i < xPos.length; i++) {
float endX = xPos[i];
float endY = yPos[i];
float stepX = (endX - startX) / STEP_COUNT;
float stepY = (endY - startY) / STEP_COUNT;
float stepT = duration / STEP_COUNT;
for (int j = 0; j < STEP_COUNT; j++) {
long deltaT = Math.round(j * stepT);
long deltaX = Math.round(j * stepX);
long deltaY = Math.round(j * stepY);
MotionEvent moveEvent = MotionEvent.obtain(startTime, startTime + deltaT,
MotionEvent.ACTION_MOVE, startX + deltaX, startY + deltaY, 0);
result.add(moveEvent);
}
startX = endX;
startY = endY;
}
float lastX = xPos[xPos.length - 1];
float lastY = yPos[yPos.length - 1];
MotionEvent lastEvent = MotionEvent.obtain(startTime, startTime + duration,
MotionEvent.ACTION_UP, lastX, lastY, 0);
result.add(lastEvent);
return result;
}
private Interaction(@Interaction.Type int type,
float[] xPos, float[] yPos, long duration) {
mType = type;
mXPositions = xPos;
mYPositions = yPos;
mDuration = duration;
mKeyCodes = null;
}
private Interaction(int[] codes) {
mKeyCodes = codes;
mType = Type.KEY_EVENT;
mYPositions = null;
mXPositions = null;
mDuration = 0;
}
private Interaction(@Interaction.Type int type,
List<Float> xPositions, List<Float> yPositions, long duration) {
if (xPositions.size() != yPositions.size()) {
throw new IllegalArgumentException("must have equal number of x and y positions");
}
int current = 0;
mXPositions = new float[xPositions.size()];
for (float p : xPositions) {
mXPositions[current++] = p;
}
current = 0;
mYPositions = new float[yPositions.size()];
for (float p : xPositions) {
mXPositions[current++] = p;
}
mType = type;
mDuration = duration;
mKeyCodes = null;
}
}

View File

@@ -0,0 +1,31 @@
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
LOCAL_PATH := $(call my-dir)
LOCAL_SDK_VERSION := 26
include $(CLEAR_VARS)
LOCAL_CFLAGS = -Wno-unused-parameter
LOCAL_MODULE:= libnativebench
LOCAL_SRC_FILES := \
Bench.cpp \
WorkerPool.cpp \
test.cpp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

View File

@@ -0,0 +1,17 @@
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
APP_ABI := armeabi
APP_MODULES := nativebench

View File

@@ -0,0 +1,373 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android/log.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>
#include "Bench.h"
Bench::Bench()
{
mTimeBucket = NULL;
mTimeBuckets = 0;
mTimeBucketDivisor = 1;
mMemLatencyLastSize = 0;
mMemDst = NULL;
mMemSrc = NULL;
mMemLoopCount = 0;
}
Bench::~Bench()
{
}
uint64_t Bench::getTimeNanos() const
{
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000);
}
uint64_t Bench::getTimeMillis() const
{
return getTimeNanos() / 1000000;
}
void Bench::testWork(void *usr, uint32_t idx)
{
Bench *b = (Bench *)usr;
//__android_log_print(ANDROID_LOG_INFO, "bench", "test %i %p", idx, b);
float f1 = 0.f;
float f2 = 0.f;
float f3 = 0.f;
float f4 = 0.f;
float *ipk = b->mIpKernel[idx];
volatile float *src = b->mSrcBuf[idx];
volatile float *out = b->mOutBuf[idx];
//__android_log_print(ANDROID_LOG_INFO, "bench", "test %p %p %p", ipk, src, out);
do {
for (int i = 0; i < 1024; i++) {
f1 += src[i * 4] * ipk[i];
f2 += src[i * 4 + 1] * ipk[i];
f3 += src[i * 4 + 2] * ipk[i];
f4 += sqrtf(f1 + f2 + f3);
}
out[0] = f1;
out[1] = f2;
out[2] = f3;
out[3] = f4;
} while (b->incTimeBucket());
}
bool Bench::initIP() {
int workers = mWorkers.getWorkerCount();
mIpKernel = new float *[workers];
mSrcBuf = new float *[workers];
mOutBuf = new float *[workers];
for (int i = 0; i < workers; i++) {
mIpKernel[i] = new float[1024];
mSrcBuf[i] = new float[4096];
mOutBuf[i] = new float[4];
}
return true;
}
bool Bench::runPowerManagementTest(uint64_t options) {
//__android_log_print(ANDROID_LOG_INFO, "bench", "rpmt x %i", options);
mTimeBucketDivisor = 1000 * 1000; // use ms
allocateBuckets(2 * 1000);
usleep(2 * 1000 * 1000);
//__android_log_print(ANDROID_LOG_INFO, "bench", "rpmt 2 b %i", mTimeBuckets);
mTimeStartNanos = getTimeNanos();
mTimeEndNanos = mTimeStartNanos + mTimeBuckets * mTimeBucketDivisor;
memset(mTimeBucket, 0, sizeof(uint32_t) * mTimeBuckets);
bool useMT = false;
//__android_log_print(ANDROID_LOG_INFO, "bench", "rpmt 2.1 b %i", mTimeBuckets);
mTimeEndGroupNanos = mTimeStartNanos;
do {
// Advance 8ms
mTimeEndGroupNanos += 8 * 1000 * 1000;
int threads = useMT ? 1 : 0;
useMT = !useMT;
if ((options & 0x1f) != 0) {
threads = options & 0x1f;
}
//__android_log_print(ANDROID_LOG_INFO, "bench", "threads %i", threads);
mWorkers.launchWork(testWork, this, threads);
} while (mTimeEndGroupNanos <= mTimeEndNanos);
return true;
}
bool Bench::allocateBuckets(size_t bucketCount) {
if (bucketCount == mTimeBuckets) {
return true;
}
if (mTimeBucket != NULL) {
delete[] mTimeBucket;
mTimeBucket = NULL;
}
mTimeBuckets = bucketCount;
if (mTimeBuckets > 0) {
mTimeBucket = new uint32_t[mTimeBuckets];
}
return true;
}
bool Bench::init() {
mWorkers.init();
initIP();
//ALOGV("%p Launching thread(s), CPUs %i", mRSC, mWorkers.mCount + 1);
return true;
}
bool Bench::incTimeBucket() const {
uint64_t time = getTimeNanos();
uint64_t bucket = (time - mTimeStartNanos) / mTimeBucketDivisor;
if (bucket >= mTimeBuckets) {
return false;
}
__sync_fetch_and_add(&mTimeBucket[bucket], 1);
return time < mTimeEndGroupNanos;
}
void Bench::getData(float *data, size_t count) const {
if (count > mTimeBuckets) {
count = mTimeBuckets;
}
for (size_t ct = 0; ct < count; ct++) {
data[ct] = (float)mTimeBucket[ct];
}
}
bool Bench::runCPUHeatSoak(uint64_t /* options */)
{
mTimeBucketDivisor = 1000 * 1000; // use ms
allocateBuckets(1000);
mTimeStartNanos = getTimeNanos();
mTimeEndNanos = mTimeStartNanos + mTimeBuckets * mTimeBucketDivisor;
memset(mTimeBucket, 0, sizeof(uint32_t) * mTimeBuckets);
mTimeEndGroupNanos = mTimeEndNanos;
mWorkers.launchWork(testWork, this, 0);
return true;
}
float Bench::runMemoryBandwidthTest(uint64_t size)
{
uint64_t t1 = getTimeMillis();
for (size_t ct = mMemLoopCount; ct > 0; ct--) {
memcpy(mMemDst, mMemSrc, size);
}
double dt = getTimeMillis() - t1;
dt /= 1000;
double bw = ((double)size) * mMemLoopCount / dt;
bw /= 1024 * 1024 * 1024;
float targetTime = 0.2f;
if (dt > targetTime) {
mMemLoopCount = (size_t)((double)mMemLoopCount / (dt / targetTime));
}
return (float)bw;
}
float Bench::runMemoryLatencyTest(uint64_t size)
{
//__android_log_print(ANDROID_LOG_INFO, "bench", "latency %i", (int)size);
void ** sp = (void **)mMemSrc;
size_t maxIndex = size / sizeof(void *);
size_t loops = ((maxIndex / 2) & (~3));
//loops = 10;
if (size != mMemLatencyLastSize) {
__android_log_print(ANDROID_LOG_INFO, "bench", "latency build %i %i", (int)maxIndex, loops);
mMemLatencyLastSize = size;
memset((void *)mMemSrc, 0, mMemLatencyLastSize);
size_t lastIdx = 0;
for (size_t ct = 0; ct < loops; ct++) {
size_t ni = rand() * rand();
ni = ni % maxIndex;
while ((sp[ni] != NULL) || (ni == lastIdx)) {
ni++;
if (ni >= maxIndex) {
ni = 1;
}
// __android_log_print(ANDROID_LOG_INFO, "bench", "gen ni loop %i %i", lastIdx, ni);
}
// __android_log_print(ANDROID_LOG_INFO, "bench", "gen ct = %i %i %i %p %p", (int)ct, lastIdx, ni, &sp[lastIdx], &sp[ni]);
sp[lastIdx] = &sp[ni];
lastIdx = ni;
}
sp[lastIdx] = 0;
}
//__android_log_print(ANDROID_LOG_INFO, "bench", "latency testing");
uint64_t t1 = getTimeNanos();
for (size_t ct = mMemLoopCount; ct > 0; ct--) {
size_t lc = 1;
volatile void *p = sp[0];
while (p != NULL) {
// Unroll once to minimize branching overhead.
void **pn = (void **)p;
p = pn[0];
pn = (void **)p;
p = pn[0];
}
}
//__android_log_print(ANDROID_LOG_INFO, "bench", "v %i %i", loops * mMemLoopCount, v);
double dt = getTimeNanos() - t1;
double dts = dt / 1000000000;
double lat = dt / (loops * mMemLoopCount);
__android_log_print(ANDROID_LOG_INFO, "bench", "latency ret %f", lat);
float targetTime = 0.2f;
if (dts > targetTime) {
mMemLoopCount = (size_t)((double)mMemLoopCount / (dts / targetTime));
if (mMemLoopCount < 1) {
mMemLoopCount = 1;
}
}
return (float)lat;
}
bool Bench::startMemTests()
{
mMemSrc = (uint8_t *)malloc(1024*1024*64);
mMemDst = (uint8_t *)malloc(1024*1024*64);
memset(mMemSrc, 0, 1024*1024*16);
memset(mMemDst, 0, 1024*1024*16);
mMemLoopCount = 1;
uint64_t start = getTimeMillis();
while((getTimeMillis() - start) < 500) {
memcpy(mMemDst, mMemSrc, 1024);
mMemLoopCount++;
}
mMemLatencyLastSize = 0;
return true;
}
void Bench::endMemTests()
{
free(mMemSrc);
free(mMemDst);
mMemSrc = NULL;
mMemDst = NULL;
mMemLatencyLastSize = 0;
}
void Bench::GflopKernelC() {
int halfKX = (mGFlop.kernelXSize / 2);
for (int x = halfKX; x < (mGFlop.imageXSize - halfKX - 1); x++) {
const float * krnPtr = mGFlop.kernelBuffer;
float sum = 0.f;
int srcInc = mGFlop.imageXSize - mGFlop.kernelXSize;
const float * srcPtr = &mGFlop.srcBuffer[x - halfKX];
for (int ix = 0; ix < mGFlop.kernelXSize; ix++) {
sum += srcPtr[0] * krnPtr[0];
krnPtr++;
srcPtr++;
}
float * dstPtr = &mGFlop.dstBuffer[x];
dstPtr[0] = sum;
}
}
void Bench::GflopKernelC_y3() {
}
float Bench::runGFlopsTest(uint64_t /* options */)
{
mTimeBucketDivisor = 1000 * 1000; // use ms
allocateBuckets(1000);
mTimeStartNanos = getTimeNanos();
mTimeEndNanos = mTimeStartNanos + mTimeBuckets * mTimeBucketDivisor;
memset(mTimeBucket, 0, sizeof(uint32_t) * mTimeBuckets);
mTimeEndGroupNanos = mTimeEndNanos;
mWorkers.launchWork(testWork, this, 0);
// Simulate image convolve
mGFlop.kernelXSize = 27;
mGFlop.imageXSize = 1024 * 1024;
mGFlop.srcBuffer = (float *)malloc(mGFlop.imageXSize * sizeof(float));
mGFlop.dstBuffer = (float *)malloc(mGFlop.imageXSize * sizeof(float));
mGFlop.kernelBuffer = (float *)malloc(mGFlop.kernelXSize * sizeof(float));
double ops = mGFlop.kernelXSize;
ops = ops * 2.f - 1.f;
ops *= mGFlop.imageXSize;
uint64_t t1 = getTimeNanos();
GflopKernelC();
double dt = getTimeNanos() - t1;
dt /= 1000.f * 1000.f * 1000.f;
double gflops = ops / dt / 1000000000.f;
__android_log_print(ANDROID_LOG_INFO, "bench", "v %f %f %f", dt, ops, gflops);
return (float)gflops;
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_BENCH_H
#define ANDROID_BENCH_H
#include <pthread.h>
#include "WorkerPool.h"
#include <string.h>
class Bench {
public:
Bench();
~Bench();
struct GFlop {
int kernelXSize;
//int kernelYSize;
int imageXSize;
//int imageYSize;
float *srcBuffer;
float *kernelBuffer;
float *dstBuffer;
};
GFlop mGFlop;
bool init();
bool runPowerManagementTest(uint64_t options);
bool runCPUHeatSoak(uint64_t options);
bool startMemTests();
void endMemTests();
float runMemoryBandwidthTest(uint64_t options);
float runMemoryLatencyTest(uint64_t options);
float runGFlopsTest(uint64_t options);
void getData(float *data, size_t count) const;
void finish();
void setPriority(int32_t p);
void destroyWorkerThreadResources();
uint64_t getTimeNanos() const;
uint64_t getTimeMillis() const;
// Adds a work unit completed to the timeline and returns
// true if the test is ongoing, false if time is up
bool incTimeBucket() const;
protected:
WorkerPool mWorkers;
bool mExit;
bool mPaused;
static void testWork(void *usr, uint32_t idx);
private:
uint8_t * volatile mMemSrc;
uint8_t * volatile mMemDst;
size_t mMemLoopCount;
size_t mMemLatencyLastSize;
float ** mIpKernel;
float * volatile * mSrcBuf;
float * volatile * mOutBuf;
uint32_t * mTimeBucket;
uint64_t mTimeStartNanos;
uint64_t mTimeEndNanos;
uint64_t mTimeBucketDivisor;
uint32_t mTimeBuckets;
uint64_t mTimeEndGroupNanos;
bool initIP();
void GflopKernelC();
void GflopKernelC_y3();
bool allocateBuckets(size_t);
};
#endif

View File

@@ -0,0 +1,266 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "WorkerPool.h"
//#include <atomic>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <android/log.h>
//static pthread_key_t gThreadTLSKey = 0;
//static uint32_t gThreadTLSKeyCount = 0;
//static pthread_mutex_t gInitMutex = PTHREAD_MUTEX_INITIALIZER;
WorkerPool::Signal::Signal() {
mSet = true;
}
WorkerPool::Signal::~Signal() {
pthread_mutex_destroy(&mMutex);
pthread_cond_destroy(&mCondition);
}
bool WorkerPool::Signal::init() {
int status = pthread_mutex_init(&mMutex, NULL);
if (status) {
__android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool mutex init failure");
return false;
}
status = pthread_cond_init(&mCondition, NULL);
if (status) {
__android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool condition init failure");
pthread_mutex_destroy(&mMutex);
return false;
}
return true;
}
void WorkerPool::Signal::set() {
int status;
status = pthread_mutex_lock(&mMutex);
if (status) {
__android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i locking for set condition.", status);
return;
}
mSet = true;
status = pthread_cond_signal(&mCondition);
if (status) {
__android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i on set condition.", status);
}
status = pthread_mutex_unlock(&mMutex);
if (status) {
__android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i unlocking for set condition.", status);
}
}
bool WorkerPool::Signal::wait(uint64_t timeout) {
int status;
bool ret = false;
status = pthread_mutex_lock(&mMutex);
if (status) {
__android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i locking for condition.", status);
return false;
}
if (!mSet) {
if (!timeout) {
status = pthread_cond_wait(&mCondition, &mMutex);
} else {
#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
status = pthread_cond_timeout_np(&mCondition, &mMutex, timeout / 1000000);
#else
// This is safe it will just make things less reponsive
status = pthread_cond_wait(&mCondition, &mMutex);
#endif
}
}
if (!status) {
mSet = false;
ret = true;
} else {
#ifndef RS_SERVER
if (status != ETIMEDOUT) {
__android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i waiting for condition.", status);
}
#endif
}
status = pthread_mutex_unlock(&mMutex);
if (status) {
__android_log_print(ANDROID_LOG_INFO, "bench", "WorkerPool: error %i unlocking for condition.", status);
}
return ret;
}
WorkerPool::WorkerPool() {
mExit = false;
mRunningCount = 0;
mLaunchCount = 0;
mCount = 0;
mThreadId = NULL;
mNativeThreadId = NULL;
mLaunchSignals = NULL;
mLaunchCallback = NULL;
}
WorkerPool::~WorkerPool() {
__android_log_print(ANDROID_LOG_INFO, "bench", "~wp");
mExit = true;
mLaunchData = NULL;
mLaunchCallback = NULL;
mRunningCount = mCount;
__sync_synchronize();
for (uint32_t ct = 0; ct < mCount; ct++) {
mLaunchSignals[ct].set();
}
void *res;
for (uint32_t ct = 0; ct < mCount; ct++) {
pthread_join(mThreadId[ct], &res);
}
//rsAssert(__sync_fetch_and_or(&mRunningCount, 0) == 0);
free(mThreadId);
free(mNativeThreadId);
delete[] mLaunchSignals;
}
bool WorkerPool::init(int threadCount) {
int cpu = sysconf(_SC_NPROCESSORS_CONF);
if (threadCount > 0) {
cpu = threadCount;
}
if (cpu < 1) {
return false;
}
mCount = (uint32_t)cpu;
__android_log_print(ANDROID_LOG_INFO, "Bench", "ThreadLaunch %i", mCount);
mThreadId = (pthread_t *) calloc(mCount, sizeof(pthread_t));
mNativeThreadId = (pid_t *) calloc(mCount, sizeof(pid_t));
mLaunchSignals = new Signal[mCount];
mLaunchCallback = NULL;
mCompleteSignal.init();
mRunningCount = mCount;
mLaunchCount = 0;
__sync_synchronize();
pthread_attr_t threadAttr;
int status = pthread_attr_init(&threadAttr);
if (status) {
__android_log_print(ANDROID_LOG_INFO, "bench", "Failed to init thread attribute.");
return false;
}
for (uint32_t ct=0; ct < mCount; ct++) {
status = pthread_create(&mThreadId[ct], &threadAttr, helperThreadProc, this);
if (status) {
mCount = ct;
__android_log_print(ANDROID_LOG_INFO, "bench", "Created fewer than expected number of threads.");
return false;
}
}
while (__sync_fetch_and_or(&mRunningCount, 0) != 0) {
usleep(100);
}
pthread_attr_destroy(&threadAttr);
return true;
}
void * WorkerPool::helperThreadProc(void *vwp) {
WorkerPool *wp = (WorkerPool *)vwp;
uint32_t idx = __sync_fetch_and_add(&wp->mLaunchCount, 1);
wp->mLaunchSignals[idx].init();
wp->mNativeThreadId[idx] = gettid();
while (!wp->mExit) {
wp->mLaunchSignals[idx].wait();
if (wp->mLaunchCallback) {
// idx +1 is used because the calling thread is always worker 0.
wp->mLaunchCallback(wp->mLaunchData, idx);
}
__sync_fetch_and_sub(&wp->mRunningCount, 1);
wp->mCompleteSignal.set();
}
//ALOGV("RS helperThread exited %p idx=%i", dc, idx);
return NULL;
}
void WorkerPool::waitForAll() const {
}
void WorkerPool::waitFor(uint64_t) const {
}
uint64_t WorkerPool::launchWork(WorkerCallback_t cb, void *usr, int maxThreads) {
//__android_log_print(ANDROID_LOG_INFO, "bench", "lw 1");
mLaunchData = usr;
mLaunchCallback = cb;
if (maxThreads < 1) {
maxThreads = mCount;
}
if ((uint32_t)maxThreads > mCount) {
//__android_log_print(ANDROID_LOG_INFO, "bench", "launchWork max > count", maxThreads, mCount);
maxThreads = mCount;
}
//__android_log_print(ANDROID_LOG_INFO, "bench", "lw 2 %i %i %i", maxThreads, mRunningCount, mCount);
mRunningCount = maxThreads;
__sync_synchronize();
for (int ct = 0; ct < maxThreads; ct++) {
mLaunchSignals[ct].set();
}
//__android_log_print(ANDROID_LOG_INFO, "bench", "lw 3 %i", mRunningCount);
while (__sync_fetch_and_or(&mRunningCount, 0) != 0) {
//__android_log_print(ANDROID_LOG_INFO, "bench", "lw 3.1 %i", mRunningCount);
mCompleteSignal.wait();
}
//__android_log_print(ANDROID_LOG_INFO, "bench", "lw 4 %i", mRunningCount);
return 0;
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_WORKER_POOL_H
#define ANDROID_WORKER_POOL_H
#include <pthread.h>
#include <string.h>
class WorkerPool {
public:
WorkerPool();
~WorkerPool();
typedef void (*WorkerCallback_t)(void *usr, uint32_t idx);
bool init(int threadCount = -1);
int getWorkerCount() const {return mCount;}
void waitForAll() const;
void waitFor(uint64_t) const;
uint64_t launchWork(WorkerCallback_t cb, void *usr, int maxThreads = -1);
protected:
class Signal {
public:
Signal();
~Signal();
bool init();
void set();
// returns true if the signal occured
// false for timeout
bool wait(uint64_t timeout = 0);
protected:
bool mSet;
pthread_mutex_t mMutex;
pthread_cond_t mCondition;
};
bool mExit;
volatile int mRunningCount;
volatile int mLaunchCount;
uint32_t mCount;
pthread_t *mThreadId;
pid_t *mNativeThreadId;
Signal mCompleteSignal;
Signal *mLaunchSignals;
WorkerCallback_t mLaunchCallback;
void *mLaunchData;
private:
//static void * threadProc(void *);
static void * helperThreadProc(void *);
};
#endif

View File

@@ -0,0 +1,178 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include <stdio.h>
//#include <fcntl.h>
//#include <unistd.h>
#include <math.h>
#include <inttypes.h>
#include <time.h>
#include <android/log.h>
#include "jni.h"
#include "Bench.h"
#define FUNC(name) Java_com_android_benchmark_synthetic_TestInterface_##name
static uint64_t GetTime() {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000);
}
extern "C" {
jlong Java_com_android_benchmark_synthetic_TestInterface_nInit(JNIEnv *_env, jobject _this, jlong options) {
Bench *b = new Bench();
bool ret = b->init();
if (ret) {
return (jlong)b;
}
delete b;
return 0;
}
void Java_com_android_benchmark_synthetic_TestInterface_nDestroy(JNIEnv *_env, jobject _this, jlong _b) {
Bench *b = (Bench *)_b;
delete b;
}
jboolean Java_com_android_benchmark_synthetic_TestInterface_nRunPowerManagementTest(
JNIEnv *_env, jobject _this, jlong _b, jlong options) {
Bench *b = (Bench *)_b;
return b->runPowerManagementTest(options);
}
jboolean Java_com_android_benchmark_synthetic_TestInterface_nRunCPUHeatSoakTest(
JNIEnv *_env, jobject _this, jlong _b, jlong options) {
Bench *b = (Bench *)_b;
return b->runCPUHeatSoak(options);
}
float Java_com_android_benchmark_synthetic_TestInterface_nGetData(
JNIEnv *_env, jobject _this, jlong _b, jfloatArray data) {
Bench *b = (Bench *)_b;
jsize len = _env->GetArrayLength(data);
float * ptr = _env->GetFloatArrayElements(data, 0);
b->getData(ptr, len);
_env->ReleaseFloatArrayElements(data, (jfloat *)ptr, 0);
return 0;
}
jboolean Java_com_android_benchmark_synthetic_TestInterface_nMemTestStart(
JNIEnv *_env, jobject _this, jlong _b) {
Bench *b = (Bench *)_b;
return b->startMemTests();
}
float Java_com_android_benchmark_synthetic_TestInterface_nMemTestBandwidth(
JNIEnv *_env, jobject _this, jlong _b, jlong opt) {
Bench *b = (Bench *)_b;
return b->runMemoryBandwidthTest(opt);
}
float Java_com_android_benchmark_synthetic_TestInterface_nGFlopsTest(
JNIEnv *_env, jobject _this, jlong _b, jlong opt) {
Bench *b = (Bench *)_b;
return b->runGFlopsTest(opt);
}
float Java_com_android_benchmark_synthetic_TestInterface_nMemTestLatency(
JNIEnv *_env, jobject _this, jlong _b, jlong opt) {
Bench *b = (Bench *)_b;
return b->runMemoryLatencyTest(opt);
}
void Java_com_android_benchmark_synthetic_TestInterface_nMemTestEnd(
JNIEnv *_env, jobject _this, jlong _b) {
Bench *b = (Bench *)_b;
b->endMemTests();
}
float Java_com_android_benchmark_synthetic_TestInterface_nMemoryTest(
JNIEnv *_env, jobject _this, jint subtest) {
uint8_t * volatile m1 = (uint8_t *)malloc(1024*1024*64);
uint8_t * m2 = (uint8_t *)malloc(1024*1024*64);
memset(m1, 0, 1024*1024*16);
memset(m2, 0, 1024*1024*16);
//__android_log_print(ANDROID_LOG_INFO, "bench", "test %i %p %p", subtest, m1, m2);
size_t loopCount = 0;
uint64_t start = GetTime();
while((GetTime() - start) < 1000000000) {
memcpy(m1, m2, subtest);
loopCount++;
}
if (loopCount == 0) {
loopCount = 1;
}
size_t count = loopCount;
uint64_t t1 = GetTime();
while (loopCount > 0) {
memcpy(m1, m2, subtest);
loopCount--;
}
uint64_t t2 = GetTime();
double dt = t2 - t1;
dt /= 1000 * 1000 * 1000;
double bw = ((double)subtest) * count / dt;
bw /= 1024 * 1024 * 1024;
__android_log_print(ANDROID_LOG_INFO, "bench", "size %i, bw %f", subtest, bw);
free (m1);
free (m2);
return (float)bw;
}
jlong Java_com_android_benchmark_synthetic_MemoryAvailableLoad1_nMemTestMalloc(
JNIEnv *_env, jobject _this, jint bytes) {
uint8_t *p = (uint8_t *)malloc(bytes);
memset(p, 0, bytes);
return (jlong)p;
}
void Java_com_android_benchmark_synthetic_MemoryAvailableLoad1_nMemTestFree(
JNIEnv *_env, jobject _this, jlong ptr) {
free((void *)ptr);
}
jlong Java_com_android_benchmark_synthetic_MemoryAvailableLoad2_nMemTestMalloc(
JNIEnv *_env, jobject _this, jint bytes) {
return Java_com_android_benchmark_synthetic_MemoryAvailableLoad1_nMemTestMalloc(_env, _this, bytes);
}
void Java_com_android_benchmark_synthetic_MemoryAvailableLoad2_nMemTestFree(
JNIEnv *_env, jobject _this, jlong ptr) {
Java_com_android_benchmark_synthetic_MemoryAvailableLoad1_nMemTestFree(_env, _this, ptr);
}
}; // extern "C"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/upload_root"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:clipToPadding="false">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<view class="com.android.benchmark.ui.BitmapUploadActivity$UploadView"
android:id="@+id/upload_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v7.widget.CardView>
<android.support.v4.widget.Space
android:layout_height="10dp"
android:layout_width="match_parent" />
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<android.support.v4.widget.Space
android:layout_height="10dp"
android:layout_width="match_parent" />
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".app.HomeActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
</android.support.design.widget.CoordinatorLayout>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".app.HomeActivity"
android:orientation="vertical">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -0,0 +1,49 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.android.benchmark.synthetic.MemoryActivity">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Large Text"
android:id="@+id/textView_status" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text=""
android:id="@+id/textView_min" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text=""
android:id="@+id/textView_max" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text=""
android:id="@+id/textView_typical" />
<view
android:layout_width="wrap_content"
android:layout_height="wrap_content"
class="com.android.benchmark.app.PerfTimeline"
android:id="@+id/mem_timeline" />
</LinearLayout>
</RelativeLayout>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".app.HomeActivity"
android:orientation="vertical">
<TextView
android:id="@+id/score_text_view"
android:textSize="20sp"
android:textStyle="bold"
android:layout_width="match_parent"
android:layout_height="30dp"
/>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/group_name"
android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
android:textSize="17dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:paddingLeft="?android:expandableListPreferredChildPaddingLeft"
android:layout_width="match_parent"
android:layout_height="55dip">
<CheckBox
android:id="@+id/benchmark_enable_checkbox"
android:checked="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/benchmark_name"
android:textSize="17dp"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="100dp"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:clipToPadding="false"
android:background="@null">
<android.support.v7.widget.CardView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<TextView
android:id="@+id/card_text"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v7.widget.CardView>
<android.support.v4.widget.Space
android:layout_height="match_parent"
android:layout_width="10dp" />
<android.support.v7.widget.CardView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>

View File

@@ -0,0 +1,26 @@
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fragment_start_button"
android:name="com.android.benchmark.app.BenchmarkDashboardFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:layout="@layout/fragment_dashboard" />

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="@dimen/detail_backdrop_height"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="parallax" />
<ImageView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@mipmap/ic_launcher"
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ExpandableListView
android:id="@+id/test_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/start_button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/ic_play"
app:layout_anchor="@id/appbar"
app:layout_anchorGravity="bottom|right|end"
android:layout_margin="@dimen/fab_margin"
android:clickable="true"/>
</android.support.design.widget.CoordinatorLayout>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ExpandableListView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image_scroll_image"
android:scaleType="centerCrop"
android:layout_width="100dp"
android:layout_height="100dp" />
<TextView
android:id="@+id/image_scroll_text"
android:layout_gravity="right"
android:textSize="12sp"
android:paddingLeft="20dp"
android:layout_width="100dp"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:padding="8dp"
android:layout_height="match_parent">
<TextView
android:id="@+id/result_name"
android:textSize="16sp"
android:layout_gravity="left"
android:layout_width="200dp"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/result_value"
android:textSize="16sp"
android:layout_gravity="right"
android:layout_width="200dp"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/benchmark_name"
android:textSize="17sp"
android:paddingLeft="?android:listPreferredItemPaddingLeft"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@@ -0,0 +1,26 @@
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".app.HomeActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_export"
app:showAsAction="never" />
</menu>

View File

@@ -0,0 +1,5 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" tools:context="com.android.benchmark.Memory">
<item android:id="@+id/action_settings" android:title="@string/action_export"
android:orderInCategory="100" />
</menu>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,25 @@
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<resources>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>

View File

@@ -0,0 +1,22 @@
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<resources>
<!-- Root tag for benchmarks -->
<declare-styleable name="AndroidBenchmarks" />
<declare-styleable name="BenchmarkGroup">
<attr name="name" format="reference|string" />
<attr name="description" format="reference|string" />
</declare-styleable>
<declare-styleable name="Benchmark">
<attr name="name" />
<attr name="description" />
<attr name="id" format="reference" />
<attr name="category" format="enum">
<enum name="generic" value="0" />
<enum name="ui" value="1" />
<enum name="compute" value="2" />
</attr>
</declare-styleable>
<declare-styleable name="PerfTimeline"><attr name="exampleString" format="string"/>
<attr name="exampleDimension" format="dimension"/>
<attr name="exampleColor" format="color"/>
<attr name="exampleDrawable" format="color|reference"/>
</declare-styleable>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

View File

@@ -0,0 +1,27 @@
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="detail_backdrop_height">256dp</dimen>
<dimen name="card_margin">16dp</dimen>
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="app_bar_height">200dp</dimen>
<dimen name="item_width">200dp</dimen>
<dimen name="text_margin">16dp</dimen>
</resources>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<resources>
<item name="benchmark_list_view_scroll" type="id" />
<item name="benchmark_image_list_view_scroll" type="id" />
<item name="benchmark_shadow_grid" type="id" />
<item name="benchmark_text_high_hitrate" type="id" />
<item name="benchmark_text_low_hitrate" type="id" />
<item name="benchmark_edit_text_input" type="id" />
<item name="benchmark_overdraw" type="id" />
<item name="benchmark_memory_bandwidth" type="id" />
<item name="benchmark_memory_latency" type="id" />
<item name="benchmark_power_management" type="id" />
<item name="benchmark_cpu_heat_soak" type="id" />
<item name="benchmark_cpu_gflops" type="id" />
</resources>

View File

@@ -0,0 +1,51 @@
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<resources>
<string name="app_name">Benchmark</string>
<string name="action_export">Export to CSV</string>
<string name="list_view_scroll_name">List View Fling</string>
<string name="list_view_scroll_description">Tests list view fling performance</string>
<string name="image_list_view_scroll_name">Image List View Fling</string>
<string name="image_list_view_scroll_description">Tests list view fling performance with images</string>
<string name="shadow_grid_name">Shadow Grid Fling</string>
<string name="shadow_grid_description">Tests shadow grid fling performance with images</string>
<string name="text_high_hitrate_name">High-hitrate text render</string>
<string name="text_high_hitrate_description">Tests high hitrate text rendering</string>
<string name="text_low_hitrate_name">Low-hitrate text render</string>
<string name="text_low_hitrate_description">Tests low-hitrate text rendering</string>
<string name="edit_text_input_name">Edit Text Input</string>
<string name="edit_text_input_description">Tests edit text input</string>
<string name="overdraw_name">Overdraw Test</string>
<string name="overdraw_description">Tests how the device handles overdraw</string>
<string name="memory_bandwidth_name">Memory Bandwidth</string>
<string name="memory_bandwidth_description">Test device\'s memory bandwidth</string>
<string name="memory_latency_name">Memory Latency</string>
<string name="memory_latency_description">Test device\'s memory latency</string>
<string name="power_management_name">Power Management</string>
<string name="power_management_description">Test device\'s power management</string>
<string name="cpu_heat_soak_name">CPU Heat Soak</string>
<string name="cpu_heat_soak_description">How hot can we make it?</string>
<string name="cpu_gflops_name">CPU GFlops</string>
<string name="cpu_gflops_description">How many gigaflops can the device attain?</string>
<string name="benchmark_category_ui">UI</string>
<string name="benchmark_category_compute">Compute</string>
<string name="benchmark_category_generic">Generic</string>
<string name="title_activity_image_list_view_scroll">ImageListViewScroll</string>
</resources>

View File

@@ -0,0 +1,43 @@
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<style name="Widget.CardContent" parent="android:Widget">
<item name="android:paddingLeft">16dp</item>
<item name="android:paddingRight">16dp</item>
<item name="android:paddingTop">24dp</item>
<item name="android:paddingBottom">24dp</item>
<item name="android:orientation">vertical</item>
</style>
</resources>

View File

@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and limitations under the
~ License.
~
-->
<com.android.benchmark.BenchmarkGroup
xmlns:benchmark="http://schemas.android.com/apk/res-auto"
benchmark:description="Benchmarks of the Android system"
benchmark:name="Android Benchmarks">
<com.android.benchmark.Benchmark
benchmark:name="@string/list_view_scroll_name"
benchmark:id="@id/benchmark_list_view_scroll"
benchmark:category="ui"
benchmark:description="@string/list_view_scroll_description" />
<com.android.benchmark.Benchmark
benchmark:name="@string/image_list_view_scroll_name"
benchmark:id="@id/benchmark_image_list_view_scroll"
benchmark:category="ui"
benchmark:description="@string/image_list_view_scroll_description" />
<com.android.benchmark.Benchmark
benchmark:name="@string/shadow_grid_name"
benchmark:id="@id/benchmark_shadow_grid"
benchmark:category="ui"
benchmark:description="@string/shadow_grid_description" />
<com.android.benchmark.Benchmark
benchmark:name="@string/text_low_hitrate_name"
benchmark:id="@id/benchmark_text_low_hitrate"
benchmark:category="ui"
benchmark:description="@string/text_low_hitrate_description" />
<com.android.benchmark.Benchmark
benchmark:name="@string/text_high_hitrate_name"
benchmark:id="@id/benchmark_text_high_hitrate"
benchmark:category="ui"
benchmark:description="@string/text_high_hitrate_description" />
<com.android.benchmark.Benchmark
benchmark:name="@string/edit_text_input_name"
benchmark:id="@id/benchmark_edit_text_input"
benchmark:category="ui"
benchmark:description="@string/edit_text_input_description" />
<com.android.benchmark.Benchmark
benchmark:name="@string/overdraw_name"
benchmark:id="@id/benchmark_overdraw"
benchmark:category="ui"
benchmark:description="@string/overdraw_description" />
<!--
<com.android.benchmark.Benchmark
benchmark:name="@string/memory_bandwidth_name"
benchmark:id="@id/benchmark_memory_bandwidth"
benchmark:category="compute"
benchmark:description="@string/memory_bandwidth_description" />
<com.android.benchmark.Benchmark
benchmark:name="@string/memory_latency_name"
benchmark:id="@id/benchmark_memory_latency"
benchmark:category="compute"
benchmark:description="@string/memory_latency_description" />
<com.android.benchmark.Benchmark
benchmark:name="@string/power_management_name"
benchmark:id="@id/benchmark_power_management"
benchmark:category="compute"
benchmark:description="@string/power_management_description" />
<com.android.benchmark.Benchmark
benchmark:name="@string/cpu_heat_soak_name"
benchmark:id="@id/benchmark_cpu_heat_soak"
benchmark:category="compute"
benchmark:description="@string/cpu_heat_soak_description" />
<com.android.benchmark.Benchmark
benchmark:name="@string/cpu_gflops_name"
benchmark:id="@id/benchmark_cpu_gflops"
benchmark:category="compute"
benchmark:description="@string/cpu_gflops_description" />
-->
</com.android.benchmark.BenchmarkGroup>

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.benchmark;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

View File

@@ -0,0 +1,62 @@
import subprocess
import re
import threading
ATRACE_PATH="/android/catapult/systrace/systrace/systrace.py"
class AdbError(RuntimeError):
def __init__(self, arg):
self.args = arg
def am(serial, cmd, args):
if not isinstance(args, list):
args = [args]
full_args = ["am"] + [cmd] + args
__call_adb(serial, full_args, False)
def pm(serial, cmd, args):
if not isinstance(args, list):
args = [args]
full_args = ["pm"] + [cmd] + args
__call_adb(serial, full_args, False)
def dumpsys(serial, topic):
return __call_adb(serial, ["dumpsys"] + [topic], True)
def trace(serial,
tags = ["gfx", "sched", "view", "freq", "am", "wm", "power", "load", "memreclaim"],
time = "10"):
args = [ATRACE_PATH, "-e", serial, "-t" + time, "-b32768"] + tags
subprocess.call(args)
def wake(serial):
output = dumpsys(serial, "power")
wakefulness = re.search('mWakefulness=([a-zA-Z]+)', output)
if wakefulness.group(1) != "Awake":
__call_adb(serial, ["input", "keyevent", "KEYCODE_POWER"], False)
def root(serial):
subprocess.call(["adb", "-s", serial, "root"])
def pull(serial, path, dest):
subprocess.call(["adb", "-s", serial, "wait-for-device", "pull"] + [path] + [dest])
def shell(serial, cmd):
__call_adb(serial, cmd, False)
def track_logcat(serial, awaited_string, callback):
threading.Thread(target=__track_logcat, name=serial + "-waiter", args=(serial, awaited_string, callback)).start()
def __call_adb(serial, args, block):
full_args = ["adb", "-s", serial, "wait-for-device", "shell"] + args
print full_args
output = None
try:
if block:
output = subprocess.check_output(full_args)
else:
subprocess.call(full_args)
except subprocess.CalledProcessError:
raise AdbError("Error calling " + " ".join(args))
return output

View File

@@ -0,0 +1,240 @@
#!/usr/bin/python
import optparse
import sys
import sqlite3
import scipy.stats
import numpy
from math import log10, floor
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pylab
import adbutil
from devices import DEVICES
DB_PATH="/data/data/com.android.benchmark/databases/BenchmarkResults"
OUT_PATH = "db/"
QUERY_BAD_FRAME = ("select run_id, name, iteration, total_duration from ui_results "
"where total_duration >= 16 order by run_id, name, iteration")
QUERY_PERCENT_JANK = ("select run_id, name, iteration, sum(jank_frame) as jank_count, count (*) as total "
"from ui_results group by run_id, name, iteration")
SKIP_TESTS = [
# "BMUpload",
# "Low-hitrate text render",
# "High-hitrate text render",
# "Edit Text Input",
# "List View Fling"
]
INCLUDE_TESTS = [
#"BMUpload"
#"Shadow Grid Fling"
#"Image List View Fling"
#"Edit Text Input"
]
class IterationResult:
def __init__(self):
self.durations = []
self.jank_count = 0
self.total_count = 0
def get_scoremap(dbpath):
db = sqlite3.connect(dbpath)
rows = db.execute(QUERY_BAD_FRAME)
scoremap = {}
for row in rows:
run_id = row[0]
name = row[1]
iteration = row[2]
total_duration = row[3]
if not run_id in scoremap:
scoremap[run_id] = {}
if not name in scoremap[run_id]:
scoremap[run_id][name] = {}
if not iteration in scoremap[run_id][name]:
scoremap[run_id][name][iteration] = IterationResult()
scoremap[run_id][name][iteration].durations.append(float(total_duration))
for row in db.execute(QUERY_PERCENT_JANK):
run_id = row[0]
name = row[1]
iteration = row[2]
jank_count = row[3]
total_count = row[4]
if run_id in scoremap.keys() and name in scoremap[run_id].keys() and iteration in scoremap[run_id][name].keys():
scoremap[run_id][name][iteration].jank_count = long(jank_count)
scoremap[run_id][name][iteration].total_count = long(total_count)
db.close()
return scoremap
def round_to_2(val):
return val
if val == 0:
return val
return round(val , -int(floor(log10(abs(val)))) + 1)
def score_device(name, serial, pull = False, verbose = False):
dbpath = OUT_PATH + name + ".db"
if pull:
adbutil.root(serial)
adbutil.pull(serial, DB_PATH, dbpath)
scoremap = None
try:
scoremap = get_scoremap(dbpath)
except sqlite3.DatabaseError:
print "Database corrupt, fetching..."
adbutil.root(serial)
adbutil.pull(serial, DB_PATH, dbpath)
scoremap = get_scoremap(dbpath)
per_test_score = {}
per_test_sample_count = {}
global_overall = {}
for run_id in iter(scoremap):
overall = []
if len(scoremap[run_id]) < 1:
if verbose:
print "Skipping short run %s" % run_id
continue
print "Run: %s" % run_id
for test in iter(scoremap[run_id]):
if test in SKIP_TESTS:
continue
if INCLUDE_TESTS and test not in INCLUDE_TESTS:
continue
if verbose:
print "\t%s" % test
scores = []
means = []
stddevs = []
pjs = []
sample_count = 0
hit_min_count = 0
# try pooling together all iterations
for iteration in iter(scoremap[run_id][test]):
res = scoremap[run_id][test][iteration]
stddev = round_to_2(numpy.std(res.durations))
mean = round_to_2(numpy.mean(res.durations))
sample_count += len(res.durations)
pj = round_to_2(100 * res.jank_count / float(res.total_count))
score = stddev * mean * pj
score = 100 * len(res.durations) / float(res.total_count)
if score == 0:
score = 1
scores.append(score)
means.append(mean)
stddevs.append(stddev)
pjs.append(pj)
if verbose:
print "\t%s: Score = %f x %f x %f = %f (%d samples)" % (iteration, stddev, mean, pj, score, len(res.durations))
if verbose:
print "\tHit min: %d" % hit_min_count
print "\tMean Variation: %0.2f%%" % (100 * scipy.stats.variation(means))
print "\tStdDev Variation: %0.2f%%" % (100 * scipy.stats.variation(stddevs))
print "\tPJ Variation: %0.2f%%" % (100 * scipy.stats.variation(pjs))
geo_run = numpy.mean(scores)
if test not in per_test_score:
per_test_score[test] = []
if test not in per_test_sample_count:
per_test_sample_count[test] = []
sample_count /= len(scoremap[run_id][test])
per_test_score[test].append(geo_run)
per_test_sample_count[test].append(int(sample_count))
overall.append(geo_run)
if not verbose:
print "\t%s:\t%0.2f (%0.2f avg. sample count)" % (test, geo_run, sample_count)
else:
print "\tOverall:\t%0.2f (%0.2f avg. sample count)" % (geo_run, sample_count)
print ""
global_overall[run_id] = scipy.stats.gmean(overall)
print "Run Overall: %f" % global_overall[run_id]
print ""
print ""
print "Variability (CV) - %s:" % name
worst_offender_test = None
worst_offender_variation = 0
for test in per_test_score:
variation = 100 * scipy.stats.variation(per_test_score[test])
if worst_offender_variation < variation:
worst_offender_test = test
worst_offender_variation = variation
print "\t%s:\t%0.2f%% (%0.2f avg sample count)" % (test, variation, numpy.mean(per_test_sample_count[test]))
print "\tOverall: %0.2f%%" % (100 * scipy.stats.variation([x for x in global_overall.values()]))
print ""
return {
"overall": global_overall.values(),
"worst_offender_test": (name, worst_offender_test, worst_offender_variation)
}
def parse_options(argv):
usage = 'Usage: %prog [options]'
desc = 'Example: %prog'
parser = optparse.OptionParser(usage=usage, description=desc)
parser.add_option("-p", dest='pull', action="store_true")
parser.add_option("-d", dest='device', action="store")
parser.add_option("-v", dest='verbose', action="store_true")
options, categories = parser.parse_args(argv[1:])
return options
def main():
options = parse_options(sys.argv)
if options.device != None:
score_device(options.device, DEVICES[options.device], options.pull, options.verbose)
else:
device_scores = []
worst_offenders = []
for name, serial in DEVICES.iteritems():
print "======== %s =========" % name
result = score_device(name, serial, options.pull, options.verbose)
device_scores.append((name, result["overall"]))
worst_offenders.append(result["worst_offender_test"])
device_scores.sort(cmp=(lambda x, y: cmp(x[1], y[1])))
print "Ranking by max overall score:"
for name, score in device_scores:
plt.plot([0, 1, 2, 3, 4, 5], score, label=name)
print "\t%s: %s" % (name, score)
plt.ylabel("Jank %")
plt.xlabel("Iteration")
plt.title("Jank Percentage")
plt.legend()
pylab.savefig("holy.png", bbox_inches="tight")
print "Worst offender tests:"
for device, test, variation in worst_offenders:
print "\t%s: %s %.2f%%" % (device, test, variation)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,11 @@
#!/usr/bin/python
DEVICES = {
'bullhead': '00606a370e3ca155',
'volantis': 'HT4BTWV00612',
'angler': '84B5T15A29021748',
'seed': '1285c85e',
'ryu': '5A27000599',
'shamu': 'ZX1G22W24R',
}

View File

View File

@@ -0,0 +1,638 @@
## Module statistics.py
##
## Copyright (c) 2013 Steven D'Aprano <steve+python@pearwood.info>.
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.
## You may obtain a copy of the License at
##
## http://www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
"""
Basic statistics module.
This module provides functions for calculating statistics of data, including
averages, variance, and standard deviation.
Calculating averages
--------------------
================== =============================================
Function Description
================== =============================================
mean Arithmetic mean (average) of data.
median Median (middle value) of data.
median_low Low median of data.
median_high High median of data.
median_grouped Median, or 50th percentile, of grouped data.
mode Mode (most common value) of data.
================== =============================================
Calculate the arithmetic mean ("the average") of data:
>>> mean([-1.0, 2.5, 3.25, 5.75])
2.625
Calculate the standard median of discrete data:
>>> median([2, 3, 4, 5])
3.5
Calculate the median, or 50th percentile, of data grouped into class intervals
centred on the data values provided. E.g. if your data points are rounded to
the nearest whole number:
>>> median_grouped([2, 2, 3, 3, 3, 4]) #doctest: +ELLIPSIS
2.8333333333...
This should be interpreted in this way: you have two data points in the class
interval 1.5-2.5, three data points in the class interval 2.5-3.5, and one in
the class interval 3.5-4.5. The median of these data points is 2.8333...
Calculating variability or spread
---------------------------------
================== =============================================
Function Description
================== =============================================
pvariance Population variance of data.
variance Sample variance of data.
pstdev Population standard deviation of data.
stdev Sample standard deviation of data.
================== =============================================
Calculate the standard deviation of sample data:
>>> stdev([2.5, 3.25, 5.5, 11.25, 11.75]) #doctest: +ELLIPSIS
4.38961843444...
If you have previously calculated the mean, you can pass it as the optional
second argument to the four "spread" functions to avoid recalculating it:
>>> data = [1, 2, 2, 4, 4, 4, 5, 6]
>>> mu = mean(data)
>>> pvariance(data, mu)
2.5
Exceptions
----------
A single exception is defined: StatisticsError is a subclass of ValueError.
"""
__all__ = [ 'StatisticsError',
'pstdev', 'pvariance', 'stdev', 'variance',
'median', 'median_low', 'median_high', 'median_grouped',
'mean', 'mode',
]
import collections
import math
from fractions import Fraction
from decimal import Decimal
from itertools import groupby
# === Exceptions ===
class StatisticsError(ValueError):
pass
# === Private utilities ===
def _sum(data, start=0):
"""_sum(data [, start]) -> (type, sum, count)
Return a high-precision sum of the given numeric data as a fraction,
together with the type to be converted to and the count of items.
If optional argument ``start`` is given, it is added to the total.
If ``data`` is empty, ``start`` (defaulting to 0) is returned.
Examples
--------
>>> _sum([3, 2.25, 4.5, -0.5, 1.0], 0.75)
(<class 'float'>, Fraction(11, 1), 5)
Some sources of round-off error will be avoided:
>>> _sum([1e50, 1, -1e50] * 1000) # Built-in sum returns zero.
(<class 'float'>, Fraction(1000, 1), 3000)
Fractions and Decimals are also supported:
>>> from fractions import Fraction as F
>>> _sum([F(2, 3), F(7, 5), F(1, 4), F(5, 6)])
(<class 'fractions.Fraction'>, Fraction(63, 20), 4)
>>> from decimal import Decimal as D
>>> data = [D("0.1375"), D("0.2108"), D("0.3061"), D("0.0419")]
>>> _sum(data)
(<class 'decimal.Decimal'>, Fraction(6963, 10000), 4)
Mixed types are currently treated as an error, except that int is
allowed.
"""
count = 0
n, d = _exact_ratio(start)
partials = {d: n}
partials_get = partials.get
T = _coerce(int, type(start))
for typ, values in groupby(data, type):
T = _coerce(T, typ) # or raise TypeError
for n,d in map(_exact_ratio, values):
count += 1
partials[d] = partials_get(d, 0) + n
if None in partials:
# The sum will be a NAN or INF. We can ignore all the finite
# partials, and just look at this special one.
total = partials[None]
assert not _isfinite(total)
else:
# Sum all the partial sums using builtin sum.
# FIXME is this faster if we sum them in order of the denominator?
total = sum(Fraction(n, d) for d, n in sorted(partials.items()))
return (T, total, count)
def _isfinite(x):
try:
return x.is_finite() # Likely a Decimal.
except AttributeError:
return math.isfinite(x) # Coerces to float first.
def _coerce(T, S):
"""Coerce types T and S to a common type, or raise TypeError.
Coercion rules are currently an implementation detail. See the CoerceTest
test class in test_statistics for details.
"""
# See http://bugs.python.org/issue24068.
assert T is not bool, "initial type T is bool"
# If the types are the same, no need to coerce anything. Put this
# first, so that the usual case (no coercion needed) happens as soon
# as possible.
if T is S: return T
# Mixed int & other coerce to the other type.
if S is int or S is bool: return T
if T is int: return S
# If one is a (strict) subclass of the other, coerce to the subclass.
if issubclass(S, T): return S
if issubclass(T, S): return T
# Ints coerce to the other type.
if issubclass(T, int): return S
if issubclass(S, int): return T
# Mixed fraction & float coerces to float (or float subclass).
if issubclass(T, Fraction) and issubclass(S, float):
return S
if issubclass(T, float) and issubclass(S, Fraction):
return T
# Any other combination is disallowed.
msg = "don't know how to coerce %s and %s"
raise TypeError(msg % (T.__name__, S.__name__))
def _exact_ratio(x):
"""Return Real number x to exact (numerator, denominator) pair.
>>> _exact_ratio(0.25)
(1, 4)
x is expected to be an int, Fraction, Decimal or float.
"""
try:
# Optimise the common case of floats. We expect that the most often
# used numeric type will be builtin floats, so try to make this as
# fast as possible.
if type(x) is float:
return x.as_integer_ratio()
try:
# x may be an int, Fraction, or Integral ABC.
return (x.numerator, x.denominator)
except AttributeError:
try:
# x may be a float subclass.
return x.as_integer_ratio()
except AttributeError:
try:
# x may be a Decimal.
return _decimal_to_ratio(x)
except AttributeError:
# Just give up?
pass
except (OverflowError, ValueError):
# float NAN or INF.
assert not math.isfinite(x)
return (x, None)
msg = "can't convert type '{}' to numerator/denominator"
raise TypeError(msg.format(type(x).__name__))
# FIXME This is faster than Fraction.from_decimal, but still too slow.
def _decimal_to_ratio(d):
"""Convert Decimal d to exact integer ratio (numerator, denominator).
>>> from decimal import Decimal
>>> _decimal_to_ratio(Decimal("2.6"))
(26, 10)
"""
sign, digits, exp = d.as_tuple()
if exp in ('F', 'n', 'N'): # INF, NAN, sNAN
assert not d.is_finite()
return (d, None)
num = 0
for digit in digits:
num = num*10 + digit
if exp < 0:
den = 10**-exp
else:
num *= 10**exp
den = 1
if sign:
num = -num
return (num, den)
def _convert(value, T):
"""Convert value to given numeric type T."""
if type(value) is T:
# This covers the cases where T is Fraction, or where value is
# a NAN or INF (Decimal or float).
return value
if issubclass(T, int) and value.denominator != 1:
T = float
try:
# FIXME: what do we do if this overflows?
return T(value)
except TypeError:
if issubclass(T, Decimal):
return T(value.numerator)/T(value.denominator)
else:
raise
def _counts(data):
# Generate a table of sorted (value, frequency) pairs.
table = collections.Counter(iter(data)).most_common()
if not table:
return table
# Extract the values with the highest frequency.
maxfreq = table[0][1]
for i in range(1, len(table)):
if table[i][1] != maxfreq:
table = table[:i]
break
return table
# === Measures of central tendency (averages) ===
def mean(data):
"""Return the sample arithmetic mean of data.
>>> mean([1, 2, 3, 4, 4])
2.8
>>> from fractions import Fraction as F
>>> mean([F(3, 7), F(1, 21), F(5, 3), F(1, 3)])
Fraction(13, 21)
>>> from decimal import Decimal as D
>>> mean([D("0.5"), D("0.75"), D("0.625"), D("0.375")])
Decimal('0.5625')
If ``data`` is empty, StatisticsError will be raised.
"""
if iter(data) is data:
data = list(data)
n = len(data)
if n < 1:
raise StatisticsError('mean requires at least one data point')
T, total, count = _sum(data)
assert count == n
return _convert(total/n, T)
# FIXME: investigate ways to calculate medians without sorting? Quickselect?
def median(data):
"""Return the median (middle value) of numeric data.
When the number of data points is odd, return the middle data point.
When the number of data points is even, the median is interpolated by
taking the average of the two middle values:
>>> median([1, 3, 5])
3
>>> median([1, 3, 5, 7])
4.0
"""
data = sorted(data)
n = len(data)
if n == 0:
raise StatisticsError("no median for empty data")
if n%2 == 1:
return data[n//2]
else:
i = n//2
return (data[i - 1] + data[i])/2
def median_low(data):
"""Return the low median of numeric data.
When the number of data points is odd, the middle value is returned.
When it is even, the smaller of the two middle values is returned.
>>> median_low([1, 3, 5])
3
>>> median_low([1, 3, 5, 7])
3
"""
data = sorted(data)
n = len(data)
if n == 0:
raise StatisticsError("no median for empty data")
if n%2 == 1:
return data[n//2]
else:
return data[n//2 - 1]
def median_high(data):
"""Return the high median of data.
When the number of data points is odd, the middle value is returned.
When it is even, the larger of the two middle values is returned.
>>> median_high([1, 3, 5])
3
>>> median_high([1, 3, 5, 7])
5
"""
data = sorted(data)
n = len(data)
if n == 0:
raise StatisticsError("no median for empty data")
return data[n//2]
def median_grouped(data, interval=1):
"""Return the 50th percentile (median) of grouped continuous data.
>>> median_grouped([1, 2, 2, 3, 4, 4, 4, 4, 4, 5])
3.7
>>> median_grouped([52, 52, 53, 54])
52.5
This calculates the median as the 50th percentile, and should be
used when your data is continuous and grouped. In the above example,
the values 1, 2, 3, etc. actually represent the midpoint of classes
0.5-1.5, 1.5-2.5, 2.5-3.5, etc. The middle value falls somewhere in
class 3.5-4.5, and interpolation is used to estimate it.
Optional argument ``interval`` represents the class interval, and
defaults to 1. Changing the class interval naturally will change the
interpolated 50th percentile value:
>>> median_grouped([1, 3, 3, 5, 7], interval=1)
3.25
>>> median_grouped([1, 3, 3, 5, 7], interval=2)
3.5
This function does not check whether the data points are at least
``interval`` apart.
"""
data = sorted(data)
n = len(data)
if n == 0:
raise StatisticsError("no median for empty data")
elif n == 1:
return data[0]
# Find the value at the midpoint. Remember this corresponds to the
# centre of the class interval.
x = data[n//2]
for obj in (x, interval):
if isinstance(obj, (str, bytes)):
raise TypeError('expected number but got %r' % obj)
try:
L = x - interval/2 # The lower limit of the median interval.
except TypeError:
# Mixed type. For now we just coerce to float.
L = float(x) - float(interval)/2
cf = data.index(x) # Number of values below the median interval.
# FIXME The following line could be more efficient for big lists.
f = data.count(x) # Number of data points in the median interval.
return L + interval*(n/2 - cf)/f
def mode(data):
"""Return the most common data point from discrete or nominal data.
``mode`` assumes discrete data, and returns a single value. This is the
standard treatment of the mode as commonly taught in schools:
>>> mode([1, 1, 2, 3, 3, 3, 3, 4])
3
This also works with nominal (non-numeric) data:
>>> mode(["red", "blue", "blue", "red", "green", "red", "red"])
'red'
If there is not exactly one most common value, ``mode`` will raise
StatisticsError.
"""
# Generate a table of sorted (value, frequency) pairs.
table = _counts(data)
if len(table) == 1:
return table[0][0]
elif table:
raise StatisticsError(
'no unique mode; found %d equally common values' % len(table)
)
else:
raise StatisticsError('no mode for empty data')
# === Measures of spread ===
# See http://mathworld.wolfram.com/Variance.html
# http://mathworld.wolfram.com/SampleVariance.html
# http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
#
# Under no circumstances use the so-called "computational formula for
# variance", as that is only suitable for hand calculations with a small
# amount of low-precision data. It has terrible numeric properties.
#
# See a comparison of three computational methods here:
# http://www.johndcook.com/blog/2008/09/26/comparing-three-methods-of-computing-standard-deviation/
def _ss(data, c=None):
"""Return sum of square deviations of sequence data.
If ``c`` is None, the mean is calculated in one pass, and the deviations
from the mean are calculated in a second pass. Otherwise, deviations are
calculated from ``c`` as given. Use the second case with care, as it can
lead to garbage results.
"""
if c is None:
c = mean(data)
T, total, count = _sum((x-c)**2 for x in data)
# The following sum should mathematically equal zero, but due to rounding
# error may not.
U, total2, count2 = _sum((x-c) for x in data)
assert T == U and count == count2
total -= total2**2/len(data)
assert not total < 0, 'negative sum of square deviations: %f' % total
return (T, total)
def variance(data, xbar=None):
"""Return the sample variance of data.
data should be an iterable of Real-valued numbers, with at least two
values. The optional argument xbar, if given, should be the mean of
the data. If it is missing or None, the mean is automatically calculated.
Use this function when your data is a sample from a population. To
calculate the variance from the entire population, see ``pvariance``.
Examples:
>>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
>>> variance(data)
1.3720238095238095
If you have already calculated the mean of your data, you can pass it as
the optional second argument ``xbar`` to avoid recalculating it:
>>> m = mean(data)
>>> variance(data, m)
1.3720238095238095
This function does not check that ``xbar`` is actually the mean of
``data``. Giving arbitrary values for ``xbar`` may lead to invalid or
impossible results.
Decimals and Fractions are supported:
>>> from decimal import Decimal as D
>>> variance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")])
Decimal('31.01875')
>>> from fractions import Fraction as F
>>> variance([F(1, 6), F(1, 2), F(5, 3)])
Fraction(67, 108)
"""
if iter(data) is data:
data = list(data)
n = len(data)
if n < 2:
raise StatisticsError('variance requires at least two data points')
T, ss = _ss(data, xbar)
return _convert(ss/(n-1), T)
def pvariance(data, mu=None):
"""Return the population variance of ``data``.
data should be an iterable of Real-valued numbers, with at least one
value. The optional argument mu, if given, should be the mean of
the data. If it is missing or None, the mean is automatically calculated.
Use this function to calculate the variance from the entire population.
To estimate the variance from a sample, the ``variance`` function is
usually a better choice.
Examples:
>>> data = [0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]
>>> pvariance(data)
1.25
If you have already calculated the mean of the data, you can pass it as
the optional second argument to avoid recalculating it:
>>> mu = mean(data)
>>> pvariance(data, mu)
1.25
This function does not check that ``mu`` is actually the mean of ``data``.
Giving arbitrary values for ``mu`` may lead to invalid or impossible
results.
Decimals and Fractions are supported:
>>> from decimal import Decimal as D
>>> pvariance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")])
Decimal('24.815')
>>> from fractions import Fraction as F
>>> pvariance([F(1, 4), F(5, 4), F(1, 2)])
Fraction(13, 72)
"""
if iter(data) is data:
data = list(data)
n = len(data)
if n < 1:
raise StatisticsError('pvariance requires at least one data point')
ss = _ss(data, mu)
T, ss = _ss(data, mu)
return _convert(ss/n, T)
def stdev(data, xbar=None):
"""Return the square root of the sample variance.
See ``variance`` for arguments and other details.
>>> stdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])
1.0810874155219827
"""
var = variance(data, xbar)
try:
return var.sqrt()
except AttributeError:
return math.sqrt(var)
def pstdev(data, mu=None):
"""Return the square root of the population variance.
See ``pvariance`` for arguments and other details.
>>> pstdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])
0.986893273527251
"""
var = pvariance(data, mu)
try:
return var.sqrt()
except AttributeError:
return math.sqrt(var)

View File

@@ -0,0 +1,154 @@
#!/usr/bin/python
import optparse
import sys
import sqlite3
import scipy.stats
import numpy
import adbutil
from devices import DEVICES
DB_PATH="/data/data/com.android.benchmark/databases/BenchmarkResults"
OUT_PATH = "db/"
QUERY_BAD_FRAME = ("select run_id, name, total_duration from ui_results "
"where total_duration >=12 order by run_id, name")
QUERY_PERCENT_JANK = ("select run_id, name, sum(jank_frame) as jank_count, count (*) as total "
"from ui_results group by run_id, name")
class IterationResult:
def __init__(self):
self.durations = []
self.jank_count = 0
self.total_count = 0
def get_scoremap(dbpath):
db = sqlite3.connect(dbpath)
rows = db.execute(QUERY_BAD_FRAME)
scoremap = {}
for row in rows:
run_id = row[0]
name = row[1]
total_duration = row[2]
if not run_id in scoremap:
scoremap[run_id] = {}
if not name in scoremap[run_id]:
scoremap[run_id][name] = IterationResult()
scoremap[run_id][name].durations.append(float(total_duration))
for row in db.execute(QUERY_PERCENT_JANK):
run_id = row[0]
name = row[1]
jank_count = row[2]
total_count = row[3]
if run_id in scoremap.keys() and name in scoremap[run_id].keys():
scoremap[run_id][name].jank_count = long(jank_count)
scoremap[run_id][name].total_count = long(total_count)
db.close()
return scoremap
def score_device(name, serial, pull = False, verbose = False):
dbpath = OUT_PATH + name + ".db"
if pull:
adbutil.root(serial)
adbutil.pull(serial, DB_PATH, dbpath)
scoremap = None
try:
scoremap = get_scoremap(dbpath)
except sqlite3.DatabaseError:
print "Database corrupt, fetching..."
adbutil.root(serial)
adbutil.pull(serial, DB_PATH, dbpath)
scoremap = get_scoremap(dbpath)
per_test_score = {}
per_test_sample_count = {}
global_overall = {}
for run_id in iter(scoremap):
overall = []
if len(scoremap[run_id]) < 1:
if verbose:
print "Skipping short run %s" % run_id
continue
print "Run: %s" % run_id
for test in iter(scoremap[run_id]):
if verbose:
print "\t%s" % test
scores = []
sample_count = 0
res = scoremap[run_id][test]
stddev = numpy.std(res.durations)
mean = numpy.mean(res.durations)
sample_count = len(res.durations)
pj = 100 * res.jank_count / float(res.total_count)
score = stddev * mean *pj
if score == 0:
score = 1
scores.append(score)
if verbose:
print "\tScore = %f x %f x %f = %f (%d samples)" % (stddev, mean, pj, score, len(res.durations))
geo_run = scipy.stats.gmean(scores)
if test not in per_test_score:
per_test_score[test] = []
if test not in per_test_sample_count:
per_test_sample_count[test] = []
per_test_score[test].append(geo_run)
per_test_sample_count[test].append(int(sample_count))
overall.append(geo_run)
if not verbose:
print "\t%s:\t%0.2f (%0.2f avg. sample count)" % (test, geo_run, sample_count)
else:
print "\tOverall:\t%0.2f (%0.2f avg. sample count)" % (geo_run, sample_count)
print ""
global_overall[run_id] = scipy.stats.gmean(overall)
print "Run Overall: %f" % global_overall[run_id]
print ""
print ""
print "Variability (CV) - %s:" % name
for test in per_test_score:
print "\t%s:\t%0.2f%% (%0.2f avg sample count)" % (test, 100 * scipy.stats.variation(per_test_score[test]), numpy.mean(per_test_sample_count[test]))
print "\tOverall: %0.2f%%" % (100 * scipy.stats.variation([x for x in global_overall.values()]))
print ""
def parse_options(argv):
usage = 'Usage: %prog [options]'
desc = 'Example: %prog'
parser = optparse.OptionParser(usage=usage, description=desc)
parser.add_option("-p", dest='pull', action="store_true")
parser.add_option("-d", dest='device', action="store")
parser.add_option("-v", dest='verbose', action="store_true")
options, categories = parser.parse_args(argv[1:])
return options
def main():
options = parse_options(sys.argv)
if options.device != None:
score_device(options.device, DEVICES[options.device], options.pull, options.verbose)
else:
for name, serial in DEVICES.iteritems():
print "======== %s =========" % name
score_device(name, serial, options.pull, options.verbose)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,65 @@
#!/usr/bin/python
import optparse
import sys
import time
import adbutil
from devices import DEVICES
def parse_options(argv):
usage = 'Usage: %prog [options]'
desc = 'Example: %prog'
parser = optparse.OptionParser(usage=usage, description=desc)
parser.add_option("-c", dest='clear', action="store_true")
parser.add_option("-d", dest='device', action="store",)
parser.add_option("-t", dest='trace', action="store_true")
options, categories = parser.parse_args(argv[1:])
return (options, categories)
def clear_data(device = None):
if device != None:
dev = DEVICES[device]
adbutil.root(dev)
adbutil.pm(dev, "clear", "com.android.benchmark")
else:
for name, dev in DEVICES.iteritems():
print("Clearing " + name)
adbutil.root(dev)
adbutil.pm(dev, "clear", "com.android.benchmark")
def start_device(name, dev):
print("Go " + name + "!")
try:
adbutil.am(dev, "force-stop", "com.android.benchmark")
adbutil.wake(dev)
adbutil.am(dev, "start",
["-n", "\"com.android.benchmark/.app.RunLocalBenchmarksActivity\"",
"--eia", "\"com.android.benchmark.EXTRA_ENABLED_BENCHMARK_IDS\"", "\"0,1,2,3,4,5,6\"",
"--ei", "\"com.android.benchmark.EXTRA_RUN_COUNT\"", "\"5\""])
except adbutil.AdbError:
print "Couldn't launch " + name + "."
def start_benchmark(device, trace):
if device != None:
start_device(device, DEVICES[device])
if trace:
time.sleep(3)
adbutil.trace(DEVICES[device])
else:
if trace:
print("Note: -t only valid with -d, can't trace")
for name, dev in DEVICES.iteritems():
start_device(name, dev)
def main():
options, categories = parse_options(sys.argv)
if options.clear:
print options.device
clear_data(options.device)
else:
start_benchmark(options.device, options.trace)
if __name__ == "__main__":
main()