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