FrameworkPerfActivity.java revision d4c4b76889f2bd2e2e34ba9fc835370020524ded
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.frameworkperf; 18 19import android.app.Activity; 20import android.content.Context; 21import android.content.Intent; 22import android.content.pm.PackageManager; 23import android.graphics.Bitmap; 24import android.graphics.BitmapFactory; 25import android.os.Bundle; 26import android.os.Handler; 27import android.os.Looper; 28import android.os.PowerManager; 29import android.os.Process; 30import android.os.SystemClock; 31import android.util.DisplayMetrics; 32import android.util.Log; 33import android.view.LayoutInflater; 34import android.view.View; 35import android.view.WindowManager; 36import android.widget.TextView; 37 38import java.util.ArrayList; 39 40/** 41 * So you thought sync used up your battery life. 42 */ 43public class FrameworkPerfActivity extends Activity { 44 final Handler mHandler = new Handler(); 45 46 TextView mLog; 47 PowerManager.WakeLock mPartialWakeLock; 48 49 long mMaxRunTime = 5000; 50 boolean mStarted; 51 52 final TestRunner mRunner = new TestRunner(); 53 54 final Op[] mOpPairs = new Op[] { 55 new MethodCallOp(), new NoOp(), 56 new MethodCallOp(), new CpuOp(), 57 new MethodCallOp(), new SchedulerOp(), 58 new MethodCallOp(), new GcOp(), 59 new SchedulerOp(), new SchedulerOp(), 60 new GcOp(), new NoOp(), 61 new IpcOp(), new NoOp(), 62 new IpcOp(), new CpuOp(), 63 new IpcOp(), new SchedulerOp(), 64 new IpcOp(), new GcOp(), 65 new ParseXmlResOp(), new NoOp(), 66 new ParseLargeXmlResOp(), new NoOp(), 67 new LayoutInflaterOp(), new NoOp(), 68 new LayoutInflaterLargeOp(), new NoOp(), 69 new LoadSmallBitmapOp(), new NoOp(), 70 new LoadLargeBitmapOp(), new NoOp(), 71 new LoadSmallScaledBitmapOp(), new NoOp(), 72 new LoadLargeScaledBitmapOp(), new NoOp(), 73 }; 74 75 final Op[] mAvailOps = new Op[] { 76 new NoOp(), 77 new CpuOp(), 78 new SchedulerOp(), 79 new MethodCallOp(), 80 new IpcOp(), 81 new ParseXmlResOp(), 82 new ParseLargeXmlResOp(), 83 new LoadSmallBitmapOp(), 84 new LoadLargeBitmapOp(), 85 new LoadSmallScaledBitmapOp(), 86 new LoadLargeScaledBitmapOp(), 87 }; 88 89 int mCurOpIndex = 0; 90 91 class RunResult { 92 final String name; 93 final String fgLongName; 94 final String bgLongName; 95 final long fgTime; 96 final long fgOps; 97 final long bgTime; 98 final long bgOps; 99 100 RunResult(TestRunner op) { 101 name = op.getName(); 102 fgLongName = op.getForegroundLongName(); 103 bgLongName = op.getBackgroundLongName(); 104 fgTime = op.getForegroundTime(); 105 fgOps = op.getForegroundOps(); 106 bgTime = op.getBackgroundTime(); 107 bgOps = op.getBackgroundOps(); 108 } 109 110 float getFgMsPerOp() { 111 return fgOps != 0 ? (fgTime / (float)fgOps) : 0; 112 } 113 114 float getBgMsPerOp() { 115 return bgOps != 0 ? (bgTime / (float)bgOps) : 0; 116 } 117 } 118 119 final ArrayList<RunResult> mResults = new ArrayList<RunResult>(); 120 121 @Override 122 public void onCreate(Bundle savedInstanceState) { 123 super.onCreate(savedInstanceState); 124 125 // Set the layout for this activity. You can find it 126 // in res/layout/hello_activity.xml 127 setContentView(R.layout.main); 128 129 findViewById(R.id.start).setOnClickListener(new View.OnClickListener() { 130 @Override public void onClick(View v) { 131 startRunning(); 132 } 133 }); 134 findViewById(R.id.stop).setOnClickListener(new View.OnClickListener() { 135 @Override public void onClick(View v) { 136 stopRunning(); 137 } 138 }); 139 mLog = (TextView)findViewById(R.id.log); 140 141 PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE); 142 mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Scheduler"); 143 mPartialWakeLock.setReferenceCounted(false); 144 } 145 146 @Override 147 public void onResume() { 148 super.onResume(); 149 } 150 151 @Override 152 public void onDestroy() { 153 super.onDestroy(); 154 stopRunning(); 155 if (mPartialWakeLock.isHeld()) { 156 mPartialWakeLock.release(); 157 } 158 } 159 160 void startCurOp() { 161 mRunner.run(mHandler, mOpPairs[mCurOpIndex], mOpPairs[mCurOpIndex+1], new Runnable() { 162 @Override public void run() { 163 RunResult result = new RunResult(mRunner); 164 log(String.format("%s: fg=%d*%gms/op (%dms) / bg=%d*%gms/op (%dms)", 165 result.name, result.fgOps, result.getFgMsPerOp(), result.fgTime, 166 result.bgOps, result.getBgMsPerOp(), result.bgTime)); 167 mResults.add(result); 168 if (!mStarted) { 169 log("Stop"); 170 stopRunning(); 171 return; 172 } 173 mCurOpIndex+=2; 174 if (mCurOpIndex >= mOpPairs.length) { 175 log("Finished"); 176 stopRunning(); 177 return; 178 } 179 startCurOp(); 180 } 181 }); 182 } 183 184 void startRunning() { 185 if (!mStarted) { 186 log("Start"); 187 mStarted = true; 188 updateWakeLock(); 189 startService(new Intent(this, SchedulerService.class)); 190 mCurOpIndex = 0; 191 mResults.clear(); 192 startCurOp(); 193 } 194 } 195 196 void stopRunning() { 197 if (mStarted) { 198 mStarted = false; 199 updateWakeLock(); 200 stopService(new Intent(this, SchedulerService.class)); 201 for (int i=0; i<mResults.size(); i++) { 202 RunResult result = mResults.get(i); 203 float fgMsPerOp = result.getFgMsPerOp(); 204 float bgMsPerOp = result.getBgMsPerOp(); 205 String fgMsPerOpStr = fgMsPerOp != 0 ? Float.toString(fgMsPerOp) : ""; 206 String bgMsPerOpStr = bgMsPerOp != 0 ? Float.toString(bgMsPerOp) : ""; 207 Log.i("Perf", "\t" + result.name + "\t" + result.fgOps 208 + "\t" + result.getFgMsPerOp() + "\t" + result.fgTime 209 + "\t" + result.fgLongName + "\t" + result.bgOps 210 + "\t" + result.getBgMsPerOp() + "\t" + result.bgTime 211 + "\t" + result.bgLongName); 212 } 213 } 214 } 215 216 void updateWakeLock() { 217 if (mStarted) { 218 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 219 if (!mPartialWakeLock.isHeld()) { 220 mPartialWakeLock.acquire(); 221 } 222 } else { 223 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 224 if (mPartialWakeLock.isHeld()) { 225 mPartialWakeLock.release(); 226 } 227 } 228 } 229 230 void log(String s) { 231 mLog.setText(mLog.getText() + "\n" + s); 232 Log.i("Perf", s); 233 } 234 235 enum BackgroundMode { 236 NOTHING, 237 CPU, 238 SCHEDULER 239 }; 240 241 public class TestRunner { 242 Handler mHandler; 243 Op mForegroundOp; 244 Op mBackgroundOp; 245 Runnable mDoneCallback; 246 247 RunnerThread mBackgroundThread; 248 RunnerThread mForegroundThread; 249 long mStartTime; 250 251 boolean mBackgroundRunning; 252 boolean mForegroundRunning; 253 254 long mBackgroundEndTime; 255 long mBackgroundOps; 256 long mForegroundEndTime; 257 long mForegroundOps; 258 259 public TestRunner() { 260 } 261 262 public String getForegroundName() { 263 return mForegroundOp.getName(); 264 } 265 266 public String getBackgroundName() { 267 return mBackgroundOp.getName(); 268 } 269 270 public String getName() { 271 String fgName = mForegroundOp.getName(); 272 String bgName = mBackgroundOp.getName(); 273 StringBuilder res = new StringBuilder(); 274 if (fgName != null) { 275 res.append(fgName); 276 res.append("Fg"); 277 } 278 if (bgName != null) { 279 res.append(bgName); 280 res.append("Bg"); 281 } 282 return res.toString(); 283 } 284 285 public String getForegroundLongName() { 286 return mForegroundOp.getLongName(); 287 } 288 289 public String getBackgroundLongName() { 290 return mBackgroundOp.getLongName(); 291 } 292 293 public void run(Handler handler, Op foreground, Op background, Runnable doneCallback) { 294 mHandler = handler; 295 mForegroundOp = foreground; 296 mBackgroundOp = background; 297 mDoneCallback = doneCallback; 298 mBackgroundThread = new RunnerThread("background", new Runnable() { 299 @Override public void run() { 300 boolean running; 301 int ops = 0; 302 do { 303 running = mBackgroundOp.onRun(); 304 ops++; 305 } while (evalRepeat(running, true) && running); 306 mBackgroundEndTime = SystemClock.uptimeMillis(); 307 mBackgroundOps = ops * mBackgroundOp.getOpsPerRun(); 308 threadFinished(false); 309 } 310 }, Process.THREAD_PRIORITY_BACKGROUND); 311 mForegroundThread = new RunnerThread("background", new Runnable() { 312 @Override public void run() { 313 boolean running; 314 int ops = 0; 315 do { 316 running = mForegroundOp.onRun(); 317 ops++; 318 } while (evalRepeat(true, running) && running); 319 mForegroundEndTime = SystemClock.uptimeMillis(); 320 mForegroundOps = ops * mForegroundOp.getOpsPerRun(); 321 threadFinished(true); 322 } 323 }, Process.THREAD_PRIORITY_FOREGROUND); 324 325 mForegroundOp.onInit(FrameworkPerfActivity.this); 326 mBackgroundOp.onInit(FrameworkPerfActivity.this); 327 328 synchronized (this) { 329 mStartTime = SystemClock.uptimeMillis(); 330 mBackgroundRunning = true; 331 mForegroundRunning = true; 332 } 333 334 mBackgroundThread.start(); 335 mForegroundThread.start(); 336 } 337 338 public long getForegroundTime() { 339 return mForegroundEndTime-mStartTime; 340 } 341 342 public long getForegroundOps() { 343 return mForegroundOps; 344 } 345 346 public long getBackgroundTime() { 347 return mBackgroundEndTime-mStartTime; 348 } 349 350 public long getBackgroundOps() { 351 return mBackgroundOps; 352 } 353 354 private boolean evalRepeat(boolean bgRunning, boolean fgRunning) { 355 synchronized (this) { 356 if (!bgRunning) { 357 mBackgroundRunning = false; 358 } 359 if (!fgRunning) { 360 mForegroundRunning = false; 361 } 362 if (!mBackgroundRunning && !mForegroundRunning) { 363 return false; 364 } 365 long now = SystemClock.uptimeMillis(); 366 if (now > (mStartTime+mMaxRunTime)) { 367 return false; 368 } 369 return true; 370 } 371 } 372 373 private void threadFinished(boolean foreground) { 374 synchronized (this) { 375 if (foreground) { 376 mForegroundRunning = false; 377 } else { 378 mBackgroundRunning = false; 379 } 380 if (!mBackgroundRunning && !mForegroundRunning) { 381 mHandler.post(new Runnable() { 382 @Override public void run() { 383 if (mDoneCallback != null) { 384 mDoneCallback.run(); 385 } 386 } 387 }); 388 } 389 } 390 } 391 } 392 393 class RunnerThread extends Thread { 394 private final Runnable mOp; 395 private final int mPriority; 396 397 RunnerThread(String name, Runnable op, int priority) { 398 super(name); 399 mOp = op; 400 mPriority = priority; 401 } 402 403 public void run() { 404 Process.setThreadPriority(mPriority); 405 mOp.run(); 406 } 407 } 408 409 static public abstract class Op { 410 final String mName; 411 final String mLongName; 412 413 public Op(String name, String longName) { 414 mName = name; 415 mLongName = longName; 416 } 417 418 public String getName() { 419 return mName; 420 } 421 422 public String getLongName() { 423 return mLongName; 424 } 425 426 void onInit(Context context) { 427 } 428 429 abstract boolean onRun(); 430 431 int getOpsPerRun() { 432 return 1; 433 } 434 } 435 436 static class NoOp extends Op { 437 NoOp() { 438 super(null, "Nothing"); 439 } 440 441 boolean onRun() { 442 return false; 443 } 444 445 int getOpsPerRun() { 446 return 0; 447 } 448 } 449 450 static class CpuOp extends Op { 451 CpuOp() { 452 super("CPU", "Consume CPU"); 453 } 454 455 boolean onRun() { 456 return true; 457 } 458 } 459 460 static class SchedulerOp extends Op { 461 SchedulerOp() { 462 super("Sched", "Change scheduler group"); 463 } 464 465 boolean onRun() { 466 Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); 467 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 468 return true; 469 } 470 } 471 472 static class GcOp extends Op { 473 GcOp() { 474 super("Gc", "Run garbage collector"); 475 } 476 477 boolean onRun() { 478 byte[] stuff = new byte[1024*1024]; 479 return true; 480 } 481 } 482 483 static class MethodCallOp extends Op { 484 MethodCallOp() { 485 super("MethodCall", "Method call"); 486 } 487 488 boolean onRun() { 489 final int N = getOpsPerRun(); 490 for (int i=0; i<N; i++) { 491 someFunc(i); 492 } 493 return true; 494 } 495 496 int someFunc(int foo) { 497 return 0; 498 } 499 500 int getOpsPerRun() { 501 return 500; 502 } 503 } 504 505 static class IpcOp extends Op { 506 PackageManager mPm; 507 String mProcessName; 508 509 IpcOp() { 510 super("Ipc", "IPC to system process"); 511 } 512 513 void onInit(Context context) { 514 mPm = context.getPackageManager(); 515 mProcessName = context.getApplicationInfo().processName; 516 } 517 518 boolean onRun() { 519 final int N = getOpsPerRun(); 520 for (int i=0; i<N; i++) { 521 mPm.queryContentProviders(mProcessName, Process.myUid(), 0); 522 } 523 return true; 524 } 525 526 int getOpsPerRun() { 527 return 100; 528 } 529 } 530 531 static class ParseXmlResOp extends Op { 532 Context mContext; 533 534 ParseXmlResOp() { 535 super("ParseXmlRes", "Parse compiled XML resource"); 536 } 537 538 void onInit(Context context) { 539 mContext = context; 540 } 541 542 boolean onRun() { 543 SimpleInflater inf = new SimpleInflater(mContext); 544 inf.inflate(R.xml.simple); 545 return true; 546 } 547 } 548 549 static class ParseLargeXmlResOp extends Op { 550 Context mContext; 551 552 ParseLargeXmlResOp() { 553 super("ParseLargeXmlRes", "Parse large XML resource"); 554 } 555 556 void onInit(Context context) { 557 mContext = context; 558 } 559 560 boolean onRun() { 561 SimpleInflater inf = new SimpleInflater(mContext); 562 inf.inflate(R.xml.simple_large); 563 return true; 564 } 565 } 566 567 static class LayoutInflaterOp extends Op { 568 Context mContext; 569 570 LayoutInflaterOp() { 571 super("LayoutInflaterOp", "Inflate layout resource"); 572 } 573 574 void onInit(Context context) { 575 mContext = context; 576 } 577 578 boolean onRun() { 579 if (Looper.myLooper() == null) { 580 Looper.prepare(); 581 } 582 LayoutInflater inf = (LayoutInflater)mContext.getSystemService( 583 Context.LAYOUT_INFLATER_SERVICE); 584 inf.inflate(R.layout.small_layout, null); 585 return true; 586 } 587 } 588 589 static class LayoutInflaterLargeOp extends Op { 590 Context mContext; 591 592 LayoutInflaterLargeOp() { 593 super("LayoutInflaterLargeOp", "Inflate large layout resource"); 594 } 595 596 void onInit(Context context) { 597 mContext = context; 598 } 599 600 boolean onRun() { 601 if (Looper.myLooper() == null) { 602 Looper.prepare(); 603 } 604 LayoutInflater inf = (LayoutInflater)mContext.getSystemService( 605 Context.LAYOUT_INFLATER_SERVICE); 606 inf.inflate(R.layout.large_layout, null); 607 return true; 608 } 609 } 610 611 static class LoadSmallBitmapOp extends Op { 612 Context mContext; 613 614 LoadSmallBitmapOp() { 615 super("LoadSmallBitmap", "Load small raw bitmap"); 616 } 617 618 void onInit(Context context) { 619 mContext = context; 620 } 621 622 boolean onRun() { 623 BitmapFactory.Options opts = new BitmapFactory.Options(); 624 opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE; 625 Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(), 626 R.drawable.stat_sample, opts); 627 bm.recycle(); 628 return true; 629 } 630 } 631 632 static class LoadLargeBitmapOp extends Op { 633 Context mContext; 634 635 LoadLargeBitmapOp() { 636 super("LoadLargeBitmap", "Load large raw bitmap"); 637 } 638 639 void onInit(Context context) { 640 mContext = context; 641 } 642 643 boolean onRun() { 644 BitmapFactory.Options opts = new BitmapFactory.Options(); 645 opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE; 646 Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(), 647 R.drawable.wallpaper_goldengate, opts); 648 bm.recycle(); 649 return true; 650 } 651 } 652 653 static class LoadSmallScaledBitmapOp extends Op { 654 Context mContext; 655 656 LoadSmallScaledBitmapOp() { 657 super("LoadSmallScaledBitmap", "Load small raw bitmap that is scaled for density"); 658 } 659 660 void onInit(Context context) { 661 mContext = context; 662 } 663 664 boolean onRun() { 665 BitmapFactory.Options opts = new BitmapFactory.Options(); 666 opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE; 667 Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(), 668 R.drawable.stat_sample_scale, opts); 669 bm.recycle(); 670 return true; 671 } 672 } 673 674 static class LoadLargeScaledBitmapOp extends Op { 675 Context mContext; 676 677 LoadLargeScaledBitmapOp() { 678 super("LoadLargeScaledBitmap", "Load large raw bitmap that is scaled for density"); 679 } 680 681 void onInit(Context context) { 682 mContext = context; 683 } 684 685 boolean onRun() { 686 BitmapFactory.Options opts = new BitmapFactory.Options(); 687 opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE; 688 Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(), 689 R.drawable.wallpaper_goldengate_scale, opts); 690 bm.recycle(); 691 return true; 692 } 693 } 694} 695