├── .gitignore ├── README.md ├── app ├── Android.mk ├── Application.mk ├── build.gradle ├── jniLibs │ └── local │ │ └── armeabi-v7a │ │ └── libroofline.so ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── google │ │ └── gables │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── GPUkernel_template.comp │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── google │ │ │ └── gables │ │ │ ├── CPURoofline.java │ │ │ ├── DSPRoofline.java │ │ │ ├── GPURoofline.java │ │ │ ├── MainActivity.java │ │ │ ├── Roofline.java │ │ │ ├── SOCRoofline.java │ │ │ ├── Utils.java │ │ │ └── utils │ │ │ └── NonSwipeViewPager.java │ ├── jni │ │ └── roofline │ │ │ ├── CPUdriver.cpp │ │ │ ├── CPUkernel.cpp │ │ │ ├── GPUdriver.cpp │ │ │ ├── Utils.cpp │ │ │ └── inc │ │ │ ├── CPUdriver.hpp │ │ │ ├── CPUkernel.hpp │ │ │ ├── GPUdriver.hpp │ │ │ ├── Utils.hpp │ │ │ └── rep.hpp │ ├── python │ │ └── gables │ │ │ ├── __init__.py │ │ │ └── gables_processor.py │ └── res │ │ ├── drawable-v24 │ │ ├── harvard_edge.png │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── fragment_cpu_roofline.xml │ │ ├── fragment_dsp_roofline.xml │ │ ├── fragment_gpu_roofline.xml │ │ ├── fragment_main.xml │ │ └── fragment_mixer_roofline.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── integers.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── google │ └── gables │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images └── CPURooflineExamplePlot.png ├── settings.gradle ├── step_by_step.md └── utils ├── expprep.sh └── plotting ├── gnuplots ├── band-vs-wss.gnu.template ├── band-vs-wss_max.gnu.template ├── flops-vs-wss.gnu.template ├── roofline.gnu.template └── secs-vs-trials.gnu.template └── scripts └── gables.py /.gitignore: -------------------------------------------------------------------------------- 1 | #built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Windows thumbnail db 19 | Thumbs.db 20 | 21 | # Android Studio 22 | *.iml 23 | .idea 24 | .gradle 25 | build/ 26 | .navigation 27 | captures/ 28 | output.json 29 | 30 | #NDK 31 | obj/ 32 | .externalNativeBuild 33 | 34 | # Crashlytics configuations 35 | com_crashlytics_export_strings.xml 36 | 37 | # Signing files 38 | .signing/ 39 | 40 | # OS-specific files 41 | .DS_Store 42 | .DS_Store? 43 | ._* 44 | .Spotlight-V100 45 | .Trashes 46 | ehthumbs.db 47 | Thumbs.db 48 | 49 | # Gables specific 50 | gnuplots/ 51 | output/ 52 | *.gables 53 | *.o 54 | *.o.d 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gables 2 | 3 | Welcome to the [_Gables: Roofline Model for Mobile SoCs._](http://research.cs.wisc.edu/multifacet/gables/) The code that is included in here is the code that is used to generate the roofline model for mobile SoCs CPUs/GPUs and DSP. The code has been tested primarily on the following two devices: (1) a Qualcomm 835 Development Kit and (2) Pixel 2/3 smartphones, but it should work on other devices. 4 | 5 |

6 | 7 |

8 | 9 | The source code that is included here can be built into an Android APK, pushed to the device and then run by a user. You can import the git repo directly from the Android Studio. 10 | 11 | # Prerequisites 12 | 13 | * You must be familiar with Android Studio to build the APK. 14 | * You have a device based on the Qualcomm chipset. 15 | * You must grant the app read/write permissions 16 | 17 | # Step-by-step 18 | 19 | ## Install Android Studio 20 | 21 | You can download the latest version of Android Studio [here](https://developer.android.com/studio/), and follow the instructions to install. 22 | 23 | ## Import the GitHub Repo 24 | 25 | You can directly import GitHub projects into Android Studio. In the Android Studio window, click VCS -> Checkout -> Git and import this repository address `https://github.com/harvard-edge/Gables`. The GitHub repo will be created as a new project in android studio. Alternatively, you can download the repo to your filesystem and manually import it as a new project into Android Studio. We recommend using the Git repo directly as you can get the latest updates. 26 | 27 | ## Build the APK 28 | 29 | Android Studio should automatically recognize the project is using the Gradle build system. If so you should be able to press `Build>Make Project` and it will attempt to build the project. 30 | 31 | We use CMake to build the C++ compenents of the app which interface with the Java code through the JNI. In order to build the app you should have CMake and the Android NDK installed and in your path. Usually Android studio will offer to install these automatically if you attempt to build without these available. 32 | 33 | ## Install the APK 34 | 35 | Once the build has completed successfully, you will be able to press `Run>Run 'app'` in the toolbar and then Android Studio will prompt you to select a device to install the APK on. 36 | 37 | We recommend running Gables on a real world device for testing. The device should have [developer options](https://developer.android.com/studio/debug/dev-options) enabled and USB debugging turned on. The device will then appear in the run prompt. Selecting this device will install the APK on it. 38 | 39 | If the name of the device doesn't appear correctly in the prompt, check that the device is conencted in the correct file transfer mode (this can usually be changed in a persistent notification on the device itself). The correct file transfer mode varies depending on device but once the correct mode is selected the device name will be visible in the run prompt. 40 | 41 | If you do wish to run Gables on an emulator, we currently only support emulator images based on the armeabi-v7a, or arm64-v8a instruction sets. Support for other architectures is coming soon. 42 | 43 | ## Run the APK 44 | 45 | The Gables app icon should now be visible in the launcher of the device in question. 46 | 47 | ## Generate the Plots 48 | 49 | The current version of the app first generates the data, and then processes the data offline to generate the plots. See [step-by-step](step_by_step.md) instructions on how to build, run and generate the offline plots. 50 | 51 | # Limitations and Restrictions 52 | 53 | The current version of the code has only been tested on devices that use the Qualcomm chipsets. 54 | 55 | If your smartphone or a development bboard that is based on some other chipsets (i.e., not Qualcomm chipsets), modifications may be needed to get the code running. Please contact us for help. 56 | 57 | Most users face trouble when it comes to running the code on the DSP. If you are on the phone, the DSP code will likely not work. This is because the DSP kernel library needs to be specially "signed" by Qualcomm to allow access to the DSP. So, the DSP code will not work out of the box. However, if you are on the development platform. The code should work. 58 | 59 | # FAQs 60 | 61 | > How do I install the APK? 62 | 63 | Please watch the video, or follow the step-by-step instructions on how to build and run the app. 64 | 65 | > After running the code, why is the plot not updating? 66 | 67 | Real-time plot updating is still work under progress. Please continue to use the offline plot generator. Stay tuned for updates as we are making progress toward releasing the updates soon. 68 | 69 | # Authors and Contributors 70 | 71 | * Vijay Janapa Reddi (Harvard University) 72 | * Daniel Inge (Harvard University) 73 | * Mark Hill (Univ. of Wisconsin) 74 | * Nikhita Kunati (Univ. of Wisconsin) 75 | 76 | Want to contribute something to the app? Please contact us, we would love the help! 77 | 78 | # Contact Us 79 | 80 | Please email your questions and concerns to vj@eecs.harvard.edu and dinge@college.harvard.edu. 81 | 82 | # Credits 83 | 84 | The inspiration for the mobile version of the code comes from the [CS Roofline Toolkit 85 | ](https://bitbucket.org/berkeleylab/cs-roofline-toolkit). The APK introduces updates and changes to the code, and includes numerous new code additions such as support for GPU OpenGL ES and the DSPs. 86 | 87 | -------------------------------------------------------------------------------- /app/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | include $(CLEAR_VARS) 3 | LOCAL_MODULE := roofline 4 | LOCAL_C_INCLUDES += ./src/main/jni/roofline/inc 5 | LOCAL_CFLAGS += -O3 6 | LOCAL_CFLAGS += -fopenmp 7 | LOCAL_LDFLAGS += -fopenmp 8 | LOCAL_SRC_FILES := ./src/main/jni/roofline/CPUdriver.cpp ./src/main/jni/roofline/CPUkernel.cpp ./src/main/jni/roofline/GPUdriver.cpp ./src/main/jni/roofline/Utils.cpp 9 | LOCAL_LDLIBS := -lGLESv3 -landroid -lEGL -llog 10 | include $(BUILD_SHARED_LIBRARY) 11 | 12 | -------------------------------------------------------------------------------- /app/Application.mk: -------------------------------------------------------------------------------- 1 | APP_OPTIM := release # Build the target in debug mode. 2 | APP_ABI := arm64-v8a 3 | APP_PLATFORM := android-27 4 | APP_STL := c++_static 5 | APP_BUILD_SCRIPT := Android.mk 6 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.chaquo.python' 3 | 4 | android { 5 | signingConfigs { 6 | config { 7 | keyAlias 'key0' 8 | keyPassword 'password' 9 | storeFile file('/Users/dinge/Desktop/passwordkeystore') 10 | storePassword 'password' 11 | } 12 | } 13 | compileSdkVersion 27 14 | defaultConfig { 15 | applicationId "edu.harvard.seas.edge.gables" 16 | minSdkVersion 21 17 | targetSdkVersion 27 18 | versionCode 1 19 | versionName "1.0" 20 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 21 | ndk { 22 | abiFilters 'arm64-v8a' 23 | } 24 | // Encapsulates your external native build configurations. 25 | externalNativeBuild { 26 | // Encapsulates your CMake build configurations. 27 | cmake { 28 | // these flags are set in the CMake file, since they follow *after* default AndroidStudio args 29 | } 30 | } 31 | python { 32 | pip { 33 | install "numpy==1.14.2" 34 | install "matplotlib==2.2.2" 35 | } 36 | staticProxy("gables.gables_processor") 37 | } 38 | } 39 | buildTypes { 40 | release { 41 | minifyEnabled false 42 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 43 | signingConfig signingConfigs.config 44 | debuggable false 45 | jniDebuggable false 46 | } 47 | debug { 48 | debuggable true 49 | jniDebuggable true 50 | } 51 | } 52 | sourceSets { 53 | main { 54 | jni.srcDirs = ['src/main/jni'] 55 | jniLibs.srcDirs = ['src/main/jniLibs'] 56 | } 57 | } 58 | externalNativeBuild { 59 | ndkBuild { 60 | path 'Android.mk' 61 | } 62 | } 63 | } 64 | 65 | dependencies { 66 | implementation fileTree(include: ['*.jar'], dir: 'libs') 67 | implementation 'com.android.support:appcompat-v7:27.1.1' 68 | implementation 'com.android.support:design:27.1.1' 69 | implementation 'com.android.support.constraint:constraint-layout:1.1.0' 70 | testImplementation 'junit:junit:4.12' 71 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 72 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 73 | implementation 'com.jjoe64:graphview:4.2.2' 74 | implementation 'com.android.support:cardview-v7:27.1.1' 75 | implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' 76 | implementation 'com.synnapps:carouselview:0.1.5' 77 | } 78 | -------------------------------------------------------------------------------- /app/jniLibs/local/armeabi-v7a/libroofline.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/jniLibs/local/armeabi-v7a/libroofline.so -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/google/gables/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.google.gables; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.google.gables", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/assets/GPUkernel_template.comp: -------------------------------------------------------------------------------- 1 | #version 320 es 2 | 3 | #define KERNEL1(a,b,c) ((a) = (b) + (c)) 4 | #define KERNEL2(a,b,c) ((a) = (a)*(b) + (c)) 5 | 6 | #define REP2(S) S ; S 7 | #define REP4(S) REP2(S); REP2(S) 8 | #define REP8(S) REP4(S); REP4(S) 9 | #define REP16(S) REP8(S); REP8(S) 10 | #define REP32(S) REP16(S); REP16(S) 11 | #define REP64(S) REP32(S); REP32(S) 12 | #define REP128(S) REP64(S); REP64(S) 13 | #define REP256(S) REP128(S); REP128(S) 14 | #define REP512(S) REP256(S); REP256(S) 15 | #define REP1024(S) REP512(S); REP512(S) 16 | #define REP2048(S) REP1024(S); REP1024(S) 17 | #define REP4096(S) REP2048(S); REP2048(S) 18 | #define REP8192(S) REP4096(S); REP4096(S) 19 | #define REP16384(S) REP8192(S); REP8192(S) 20 | #define REP32768(S) REP16384(S); REP16384(S) 21 | 22 | layout(local_size_x = XXX_LAYOUT_SIZE_XXX, local_size_y = 1, local_size_z = 1) in; 23 | 24 | layout(binding = 0) readonly buffer Params { 25 | uint data[]; 26 | } params; 27 | layout(binding = 1) readonly buffer IArray { 28 | float data[]; 29 | } ibuff; 30 | layout(binding = 2) writeonly buffer OArray { 31 | uint data[]; 32 | } retval; 33 | 34 | void main() 35 | { 36 | uint wsssize = params.data[0]; 37 | uint ntrials = params.data[1]; 38 | 39 | uint total_thr = gl_NumWorkGroups.x * gl_WorkGroupSize.x; 40 | uint elem_per_thr = (wsssize + (total_thr - 1u)) / total_thr; 41 | 42 | uint start_idx = gl_GlobalInvocationID.x; 43 | uint end_idx = start_idx + elem_per_thr * total_thr; 44 | 45 | uint stride_idx = total_thr; 46 | 47 | if (start_idx > wsssize) 48 | start_idx = wsssize; 49 | if (end_idx > wsssize) 50 | end_idx = wsssize; 51 | 52 | float alpha = 0.5; 53 | for (uint j = 0u; j < ntrials; ++j) { 54 | for (uint i = start_idx; i < end_idx; i += stride_idx) { 55 | float beta = 0.8; 56 | XXX_KERNEL_FLOPS_PREFIX_XXX(beta, ibuff.data[i], alpha)XXX_KERNEL_FLOPS_POSTFIX_XXX; 57 | retval.data[3] = uint(beta); 58 | } 59 | } 60 | 61 | retval.data[0] = 4u; 62 | retval.data[1] = 2u; 63 | retval.data[3] = 0u; 64 | } -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/google/gables/CPURoofline.java: -------------------------------------------------------------------------------- 1 | package com.google.gables; 2 | 3 | 4 | import android.app.Activity; 5 | import android.app.ProgressDialog; 6 | import android.content.DialogInterface; 7 | import android.graphics.Bitmap; 8 | import android.graphics.BitmapFactory; 9 | import android.graphics.Color; 10 | import android.os.AsyncTask; 11 | import android.os.Bundle; 12 | import android.support.v4.app.Fragment; 13 | import android.system.ErrnoException; 14 | import android.system.Os; 15 | import android.util.Log; 16 | import android.view.LayoutInflater; 17 | import android.view.View; 18 | import android.view.ViewGroup; 19 | import android.widget.CompoundButton; 20 | import android.widget.ImageView; 21 | import android.widget.SeekBar; 22 | import android.widget.Switch; 23 | import android.widget.TextView; 24 | import android.widget.ToggleButton; 25 | 26 | import com.github.mikephil.charting.charts.LineChart; 27 | import com.github.mikephil.charting.components.AxisBase; 28 | import com.github.mikephil.charting.components.Description; 29 | import com.github.mikephil.charting.components.XAxis; 30 | import com.github.mikephil.charting.data.Entry; 31 | import com.github.mikephil.charting.data.LineData; 32 | import com.github.mikephil.charting.data.LineDataSet; 33 | import com.github.mikephil.charting.formatter.IAxisValueFormatter; 34 | import com.github.mikephil.charting.formatter.IndexAxisValueFormatter; 35 | import com.github.mikephil.charting.formatter.ValueFormatter; 36 | import com.jjoe64.graphview.GraphView; 37 | import com.jjoe64.graphview.GridLabelRenderer; 38 | import com.jjoe64.graphview.series.DataPoint; 39 | import com.jjoe64.graphview.series.LineGraphSeries; 40 | import com.synnapps.carouselview.CarouselView; 41 | import com.synnapps.carouselview.ImageListener; 42 | 43 | import org.w3c.dom.Text; 44 | 45 | import java.io.BufferedReader; 46 | import java.io.DataOutputStream; 47 | import java.io.File; 48 | import java.io.FileNotFoundException; 49 | import java.io.FileOutputStream; 50 | import java.io.IOException; 51 | import java.io.InputStreamReader; 52 | import java.text.DecimalFormat; 53 | import java.util.ArrayList; 54 | import java.util.Arrays; 55 | import java.util.List; 56 | 57 | import gables.gables_processor.GablesPython; 58 | 59 | import static com.google.gables.Roofline.CPU_Execute; 60 | import static com.google.gables.Utils.ExtStoragePermissions_t.EXT_STORAGE_RW; 61 | import static com.google.gables.Utils.MiB; 62 | import static com.google.gables.Utils.deleteDirectory; 63 | import static com.google.gables.Utils.getNumberOfCores; 64 | import static com.google.gables.Utils.isStorageAvailable; 65 | import static com.google.gables.Utils.log; 66 | 67 | public class CPURoofline extends Fragment { 68 | private static final String TAG = CPURoofline.class.getName(); 69 | private ImageView chart; 70 | private ProgressDialog gProcessDialog; 71 | private String gResultsDir = "CPURoofline"; 72 | private CarouselView slider; 73 | private TextView slidePrompt; 74 | private View edgeLogo; 75 | 76 | @Override 77 | public void onCreate(Bundle savedInstanceState) { 78 | 79 | super.onCreate(savedInstanceState); 80 | try { 81 | Os.setenv("OMP_PROC_BIND", "spread", true); 82 | Os.setenv("OMP_PLACES", "threads", true); 83 | } catch (ErrnoException e) { 84 | e.printStackTrace(); 85 | } 86 | Log.e("shell", "hi"); 87 | Log.e("shell", shellRoofline()); 88 | Log.e("shell", "bye"); 89 | } 90 | 91 | private float scaleCbr(double cbr) { 92 | return (float)(Math.log10(cbr)); 93 | } 94 | 95 | private float unScaleCbr(double cbr) { 96 | double calcVal = Math.pow(10, cbr); 97 | return (float)(calcVal); 98 | } 99 | 100 | /** 101 | * Setup a button to trigger the activation of the roofline process 102 | * 103 | * @param rootView 104 | */ 105 | void setupButton(final View rootView) { 106 | // Register a callback on the toggle button to turn ON/OFF cpu activity 107 | final ToggleButton button = rootView.findViewById(R.id.toggle_runRoofline); 108 | button.setTextOn("Busy!"); 109 | button.setTextOff("Run"); 110 | button.setChecked(false); // set the current state of a toggle button 111 | 112 | button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 113 | 114 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 115 | 116 | if (isChecked) { // enabled 117 | final SeekBar seekBar_mem = rootView.findViewById(R.id.seekBar_threadMem); 118 | final SeekBar seekBar_cpu = rootView.findViewById(R.id.seekBar_maxThreads); 119 | final SeekBar seekBar_opi = rootView.findViewById(R.id.seekBar_opIntensity); 120 | 121 | final int maxMemory = (1 << seekBar_mem.getProgress()) * MiB; // converting to bytes 122 | final int nThreads = seekBar_cpu.getProgress() + 1; // remember that seekbar starts at 0 123 | final int nFlops = (1 << seekBar_opi.getProgress()); // remember that seekbar starts at 0 124 | 125 | // final Switch neonSwitch = rootView.findViewById(R.id.switch_neonMode); 126 | final boolean neonMode = false; 127 | 128 | CPURoofline(nThreads, maxMemory, nFlops, neonMode); 129 | 130 | button.setChecked(false); 131 | } 132 | } 133 | }); 134 | } 135 | 136 | public void displayGraph(String filename, ImageView view) { 137 | File file = new File(filename); 138 | if (file.exists()) { 139 | Bitmap myBitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); 140 | view.setImageBitmap(myBitmap); 141 | } 142 | } 143 | 144 | public String shellRoofline() { 145 | StringBuffer output = new StringBuffer(); 146 | 147 | Process p; 148 | try { 149 | p = Runtime.getRuntime().exec("su -C ./data/local/tmp/stream"); 150 | p.waitFor(); 151 | BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); 152 | 153 | String line = ""; 154 | while ((line = reader.readLine())!= null) { 155 | output.append(line + "n"); 156 | } 157 | 158 | } catch (Exception e) { 159 | Log.e("Oh no", e.getMessage()); 160 | } 161 | String response = output.toString(); 162 | return response; 163 | } 164 | 165 | public void RooflineCmdline() { 166 | File outputFile = new File("/data/data/com.google.gables/pixel.gables"); 167 | try { 168 | ProcessBuilder pb = new ProcessBuilder("/data/data/com.google.gables/gables"); 169 | pb.redirectOutput(outputFile); 170 | 171 | Log.i(TAG, "native started..."); 172 | Process process = pb.start(); 173 | BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); 174 | String line; 175 | while ((line = reader.readLine()) != null) { 176 | Log.i(TAG, line); 177 | } 178 | process.waitFor(); 179 | } catch (IOException e) { 180 | e.printStackTrace(); 181 | } catch (InterruptedException e) { 182 | e.printStackTrace(); 183 | } 184 | Log.i(TAG, "native finished..."); 185 | } 186 | 187 | private void setupCPUSlider(final View rootView) { 188 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_maxThreads); 189 | 190 | int nCores = getNumberOfCores(); 191 | mSeekBar.setProgress(nCores); 192 | mSeekBar.setMax(nCores - 1); // we start from 0, but we display +1 193 | 194 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads); 195 | textView.setText("Thread Count (" + Long.toString(mSeekBar.getProgress() + 1) + " Cores)"); // increment by 1 since bar starts from 0. 196 | 197 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runRoofline); 198 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 199 | 200 | @Override 201 | public void onStopTrackingTouch(SeekBar seekBar) { 202 | toggle.setChecked(false); 203 | } 204 | 205 | @Override 206 | public void onStartTrackingTouch(SeekBar seekBar) { 207 | toggle.setChecked(false); 208 | } 209 | 210 | @Override 211 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 212 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads); 213 | textView.setText("Thread Count (" + Long.toString(mSeekBar.getProgress() + 1) + " Cores)"); 214 | } 215 | }); 216 | } 217 | 218 | private void setupOpIntensity(final View rootView) { 219 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_opIntensity); 220 | mSeekBar.setProgress(4); //fixme debug version 221 | 222 | TextView textView = rootView.findViewById(R.id.txtBox_opIntensity); 223 | textView.setText("Ops (" + Long.toString(1 << mSeekBar.getProgress()) + " FLOPS/byte)"); 224 | 225 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runRoofline); 226 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 227 | 228 | @Override 229 | public void onStopTrackingTouch(SeekBar seekBar) { 230 | toggle.setChecked(false); 231 | } 232 | 233 | @Override 234 | public void onStartTrackingTouch(SeekBar seekBar) { 235 | toggle.setChecked(false); 236 | } 237 | 238 | @Override 239 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 240 | TextView textView = rootView.findViewById(R.id.txtBox_opIntensity); 241 | textView.setText("Ops (" + Long.toString(1 << mSeekBar.getProgress()) + " FLOPS/byte)"); 242 | } 243 | }); 244 | } 245 | 246 | private void setupMemorySlider(final View rootView) { 247 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_threadMem); 248 | 249 | TextView textView = rootView.findViewById(R.id.txtBox_threadMem); 250 | textView.setText("Memory Alloc. (" + Long.toString(1 << mSeekBar.getProgress()) + " MB)"); 251 | 252 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runRoofline); 253 | 254 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 255 | 256 | @Override 257 | public void onStopTrackingTouch(SeekBar seekBar) { 258 | toggle.setChecked(false); 259 | } 260 | 261 | @Override 262 | public void onStartTrackingTouch(SeekBar seekBar) { 263 | toggle.setChecked(false); 264 | } 265 | 266 | @Override 267 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 268 | TextView textView = rootView.findViewById(R.id.txtBox_threadMem); 269 | textView.setText("Memory Alloc. (" + Long.toString(1 << mSeekBar.getProgress()) + " MB)"); 270 | } 271 | }); 272 | } 273 | 274 | // private void setupSwitch(final View rootView) { 275 | // final Switch neonSwitch = rootView.findViewById(R.id.switch_neonMode); 276 | // final boolean neonMode = neonSwitch.isChecked(); 277 | // 278 | // neonSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 279 | // public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 280 | // TextView textView = rootView.findViewById(R.id.txtBox_neonSwitch); 281 | // if (isChecked) { 282 | // textView.setText("NEON Vectorization (Enabled)"); 283 | // } else { 284 | // //fixme: check to see how we can disable this for hardcore real. 285 | // textView.setText("NEON Vectorization (Disabled)"); 286 | // } 287 | // } 288 | // }); 289 | // } 290 | 291 | private void setupSliders(View rootView) { 292 | setupMemorySlider(rootView); 293 | setupCPUSlider(rootView); 294 | setupOpIntensity(rootView); 295 | // setupSwitch(rootView); 296 | } 297 | 298 | @Override 299 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 300 | Bundle savedInstanceState) { 301 | View rootView = inflater.inflate(R.layout.fragment_cpu_roofline, container, false); 302 | chart = rootView.findViewById(R.id.graph); 303 | setupSliders(rootView); 304 | setupButton(rootView); 305 | slider = rootView.findViewById(R.id.carouselView); 306 | slidePrompt = rootView.findViewById(R.id.swipe_prompt); 307 | edgeLogo = rootView.findViewById(R.id.edge_logo); 308 | return rootView; 309 | } 310 | 311 | private void writeToSDFile(File dir, String filename, String ostr) { 312 | 313 | boolean success = true; 314 | if (!dir.exists()) { 315 | success = dir.mkdir(); // FIXME: if this fails, then you might have to request permission in app settings. 316 | } 317 | if (success) { 318 | Log.i(TAG, "Output directory created."); 319 | } else { 320 | Log.i(TAG, "Output directory NOT created!"); 321 | } 322 | 323 | File file = new File(dir, filename); 324 | 325 | try { 326 | FileOutputStream fos = new FileOutputStream(file); 327 | fos.write(ostr.getBytes()); 328 | fos.close(); 329 | } catch (FileNotFoundException e) { 330 | e.printStackTrace(); 331 | Log.i(TAG, "******* File not found. Did you" + 332 | " add a WRITE_EXTERNAL_STORAGE permission to the manifest?"); 333 | } catch (IOException e) { 334 | e.printStackTrace(); 335 | } 336 | Log.i(TAG, "\n\nFile written to " + file); 337 | } 338 | 339 | public void CPURoofline(int nThreads, int maxMemory, int nFlops, boolean neon) { 340 | CPURooflineAsync rooflineATask = new CPURooflineAsync(getActivity(), 341 | nThreads, maxMemory, nFlops, neon); 342 | 343 | rooflineATask.execute(); 344 | } 345 | 346 | class CPURooflineAsync extends AsyncTask { 347 | final int threads, mem, flops; 348 | final boolean neon; 349 | private AsyncTask currTask = null; 350 | 351 | public CPURooflineAsync(Activity activity, int nThreads, int threadMem, int maxFlops, boolean neon) { 352 | super(); 353 | 354 | this.threads = nThreads; 355 | this.mem = threadMem; 356 | this.flops = maxFlops; 357 | this.neon = neon; 358 | 359 | gProcessDialog = new ProgressDialog(activity); 360 | } 361 | 362 | @Override 363 | protected void onPreExecute() { 364 | super.onPreExecute(); 365 | 366 | currTask = this; 367 | 368 | gProcessDialog.setMax(100); 369 | gProcessDialog.setProgress(0); 370 | gProcessDialog.setTitle("Roofline in progress"); 371 | gProcessDialog.setMessage("Starting up... "); 372 | gProcessDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 373 | gProcessDialog.setCancelable(false); 374 | gProcessDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { 375 | @Override 376 | public void onClick(DialogInterface dialog, int which) { 377 | dialog.dismiss(); 378 | currTask.cancel(true); 379 | } 380 | }); 381 | 382 | gProcessDialog.show(); 383 | } 384 | 385 | @Override 386 | protected String doInBackground(Void... params) { 387 | File root = android.os.Environment.getExternalStorageDirectory(); 388 | File odir = new File(root.getAbsolutePath() + "/" + gResultsDir + "/"); 389 | deleteDirectory(odir); 390 | Log.i(TAG, "Deleting the " + odir + " output directory."); 391 | 392 | // storage for updating progress of the work to progress bar 393 | int progressCounter = 0; 394 | final int progressTotal = (log(flops, 2) + 1) * threads; 395 | 396 | stop: 397 | // label to break out of inner loop if "cancel" was hit on progress bar. 398 | for (int i = 1; i <= flops; i = i * 2) { 399 | for (int j = threads; j <= threads; j++) { //FIXME: Fix this debug loop 400 | String strCurrProgress = String.valueOf(Math.round(100.0 * progressCounter / progressTotal)); 401 | String message = "Running " + i + " FLOPS/byte with " + j + " threads."; 402 | publishProgress(strCurrProgress, message); 403 | publishProgress(strCurrProgress, message); 404 | Log.i(TAG, "Starting " + i + " FLOPS/byte with " + j + " threads."); 405 | 406 | String output = CPU_Execute(mem, j, i, neon); 407 | 408 | generatePlotData(processRawData(output)); 409 | 410 | if (isCancelled()) { 411 | message = "Cancelling..."; 412 | publishProgress(strCurrProgress, message); 413 | Log.i(TAG, "Terminating out of the loop."); 414 | 415 | break stop; 416 | } 417 | 418 | if (isStorageAvailable(EXT_STORAGE_RW)) { 419 | message = "Writing results to storage."; 420 | publishProgress(strCurrProgress, message); 421 | 422 | String ofilename = "threads-" + j + "_flops-" + i + "_neon-" + neon + ".gables"; 423 | writeToSDFile(odir, ofilename, output); 424 | } else { 425 | Log.e(TAG, "Storage is not available for RW or is unavailable."); 426 | } 427 | 428 | // we've made progress so update 429 | progressCounter++; 430 | } 431 | } 432 | 433 | return "All done with AsyncTask!"; 434 | } 435 | 436 | @Override 437 | protected void onProgressUpdate(String... msg) { 438 | super.onProgressUpdate(msg); 439 | gProcessDialog.setProgress(Integer.parseInt(msg[0])); 440 | gProcessDialog.setMessage(msg[1]); 441 | } 442 | 443 | // onCancelled() is called when the async task is cancelled. 444 | @Override 445 | protected void onCancelled(String result) { 446 | } 447 | 448 | protected void onPostExecute(String param) { 449 | 450 | gProcessDialog.setMessage("Finished processing data."); 451 | new GablesPython().processCPURoofline(); 452 | setupSlider(); 453 | if (gProcessDialog.isShowing()) { 454 | gProcessDialog.dismiss(); 455 | } 456 | } 457 | 458 | private void setupSlider() { 459 | ImageListener imageListener = new ImageListener() { 460 | @Override 461 | public void setImageForPosition(int position, ImageView imageView) { 462 | String filename = ""; 463 | switch (position) { 464 | case 0: 465 | default: 466 | filename = "/sdcard/CPURoofline/roofline.png"; 467 | break; 468 | case 1: 469 | filename = "/sdcard/CPURoofline/bandwidth.png"; 470 | break; 471 | } 472 | displayGraph(filename, imageView); 473 | } 474 | }; 475 | slider.setImageListener(imageListener); 476 | slider.setPageCount(2); 477 | slider.setVisibility(View.VISIBLE); 478 | edgeLogo.setVisibility(View.GONE); 479 | slidePrompt.setVisibility(View.VISIBLE); 480 | } 481 | void generatePlotData(List values) { 482 | List gflops = new ArrayList<>(); 483 | List flopsPerByte = new ArrayList<>(); 484 | for (CPUDataPoint value : values) { 485 | gflops.add(value.getTotalFlops()/value.getTotalSeconds()); 486 | flopsPerByte.add(value.getTotalFlops() / value.getTotalBytes()); 487 | } 488 | System.out.println(gflops.toString()); 489 | System.out.println(flopsPerByte.toString()); 490 | } 491 | 492 | List processRawData(String data) { 493 | List rows = Arrays.asList(data.trim().split("\\s*\\n\\s*")); 494 | List points = new ArrayList<>(); 495 | for (String row : rows) { 496 | if (row.contains("META_DATA")) { 497 | break; 498 | } else { 499 | points.add(CPUDataPoint.parseCPUDataPoint(row)); 500 | } 501 | } 502 | return points; 503 | } 504 | } 505 | 506 | static class CPUDataPoint { 507 | 508 | double distinctBytes; 509 | double nTrials; 510 | double totalSeconds; 511 | double totalBytes; 512 | double totalFlops; 513 | 514 | public static CPUDataPoint parseCPUDataPoint(String input) { 515 | String[] values = input.split("\\s+"); 516 | CPUDataPoint point = new CPUDataPoint(); 517 | point.setDistinctBytes(Double.valueOf(values[0])); 518 | point.setnTrials(Double.valueOf(values[1])); 519 | point.setTotalSeconds(Double.valueOf(values[2])); 520 | point.setTotalBytes(Double.valueOf(values[3])); 521 | point.setTotalFlops(Double.valueOf(values[4])); 522 | return point; 523 | } 524 | 525 | public double getDistinctBytes() { 526 | return distinctBytes; 527 | } 528 | 529 | public void setDistinctBytes(double distinctBytes) { 530 | this.distinctBytes = distinctBytes; 531 | } 532 | 533 | public double getnTrials() { 534 | return nTrials; 535 | } 536 | 537 | public void setnTrials(double nTrials) { 538 | this.nTrials = nTrials; 539 | } 540 | 541 | public double getTotalSeconds() { 542 | return totalSeconds; 543 | } 544 | 545 | public void setTotalSeconds(double totalSeconds) { 546 | this.totalSeconds = totalSeconds; 547 | } 548 | 549 | public double getTotalBytes() { 550 | return totalBytes; 551 | } 552 | 553 | public void setTotalBytes(double totalBytes) { 554 | this.totalBytes = totalBytes; 555 | } 556 | 557 | public double getTotalFlops() { 558 | return totalFlops; 559 | } 560 | 561 | public void setTotalFlops(double totalFlops) { 562 | this.totalFlops = totalFlops; 563 | } 564 | } 565 | } 566 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/gables/DSPRoofline.java: -------------------------------------------------------------------------------- 1 | package com.google.gables; 2 | 3 | 4 | import android.app.Activity; 5 | import android.app.ProgressDialog; 6 | import android.content.DialogInterface; 7 | import android.graphics.Color; 8 | import android.os.AsyncTask; 9 | import android.os.Bundle; 10 | import android.support.v4.app.Fragment; 11 | import android.util.Log; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.widget.CompoundButton; 16 | import android.widget.SeekBar; 17 | import android.widget.Switch; 18 | import android.widget.TextView; 19 | import android.widget.ToggleButton; 20 | 21 | import com.jjoe64.graphview.GraphView; 22 | import com.jjoe64.graphview.GridLabelRenderer; 23 | import com.jjoe64.graphview.series.DataPoint; 24 | import com.jjoe64.graphview.series.LineGraphSeries; 25 | 26 | import java.io.File; 27 | import java.io.FileNotFoundException; 28 | import java.io.FileOutputStream; 29 | import java.io.IOException; 30 | 31 | import static com.google.gables.Roofline.DSP_Execute; 32 | import static com.google.gables.Roofline.DSP_NumThreads; 33 | import static com.google.gables.Utils.MiB; 34 | import static com.google.gables.Utils.deleteDirectory; 35 | 36 | public class DSPRoofline extends Fragment { 37 | private static final String TAG = DSPRoofline.class.getName(); 38 | 39 | private ProgressDialog gProcessDialog; 40 | private String gResultsDir = "DSPRoofline"; 41 | 42 | @Override 43 | public void onCreate(Bundle savedInstanceState) { 44 | 45 | super.onCreate(savedInstanceState); 46 | 47 | } 48 | 49 | void drawGraph(View rootView) { 50 | // Setup a graph view 51 | GraphView graphview = rootView.findViewById(R.id.graph); 52 | 53 | // activate horizontal zooming and scrolling 54 | graphview.getViewport().setScalable(false); 55 | graphview.getViewport().setScrollable(true); 56 | graphview.getViewport().setScalableY(false); 57 | graphview.getViewport().setScrollableY(true); 58 | 59 | LineGraphSeries series = new LineGraphSeries<>(new DataPoint[]{ 60 | new DataPoint(0, 1), 61 | new DataPoint(1, 2), 62 | new DataPoint(2, 3), 63 | new DataPoint(3, 4), 64 | new DataPoint(4, 4), 65 | new DataPoint(5, 4) 66 | }); 67 | 68 | GridLabelRenderer gridLabel = graphview.getGridLabelRenderer(); 69 | gridLabel.setHorizontalAxisTitle("Operational Intensity (FLOPS/byte)"); 70 | gridLabel.setVerticalAxisTitle("Performance (GFLOPS)"); 71 | 72 | graphview.setTitle("DSP Roofline"); 73 | 74 | series.setTitle("DSP Roofline"); 75 | series.setBackgroundColor(Color.GRAY); 76 | series.setColor(Color.BLACK); 77 | series.setDrawDataPoints(false); 78 | series.setDataPointsRadius(10); 79 | series.setThickness(7); 80 | 81 | graphview.addSeries(series); 82 | } 83 | 84 | /** 85 | * Setup a button to trigger the activation of the roofline process 86 | * 87 | * @param rootView 88 | */ 89 | void setupButton(final View rootView) { 90 | // Register a callback on the toggle button to turn ON/OFF DSP activity 91 | final ToggleButton button = rootView.findViewById(R.id.toggle_runRoofline); 92 | button.setTextOn("Busy!"); 93 | button.setTextOff("Run"); 94 | button.setChecked(false); // set the current state of a toggle button 95 | 96 | button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 97 | 98 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 99 | 100 | if (isChecked) { // enabled 101 | final SeekBar seekBar_mem = rootView.findViewById(R.id.seekBar_threadMem); 102 | final SeekBar seekBar_DSP = rootView.findViewById(R.id.seekBar_maxThreads); 103 | final SeekBar seekBar_opi = rootView.findViewById(R.id.seekBar_opIntensity); 104 | 105 | final int maxMemory = (1 << seekBar_mem.getProgress()) * MiB; // converting to bytes 106 | final int nThreads = seekBar_DSP.getProgress() + 1; // remember that seekbar starts at 0 107 | final int nFlops = (1 << seekBar_opi.getProgress()); // remember that seekbar starts at 0 108 | 109 | final Switch hvxSwitch = rootView.findViewById(R.id.switch_hvxMode); 110 | final boolean hvxMode = hvxSwitch.isChecked(); 111 | 112 | DSPRoofline(nThreads, maxMemory, nFlops, hvxMode); 113 | 114 | button.setChecked(false); 115 | } 116 | } 117 | }); 118 | } 119 | 120 | private void setupThreadCountSlider(final View rootView) { 121 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_maxThreads); 122 | 123 | final Switch toggle = rootView.findViewById(R.id.switch_hvxMode); 124 | 125 | int nCores = DSP_NumThreads(toggle.isChecked()); 126 | mSeekBar.setProgress(nCores); 127 | mSeekBar.setMax(nCores - 1); // we start from 0, but we display +1 128 | 129 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads); 130 | textView.setText("Thread Count (" + Long.toString(mSeekBar.getProgress() + 1) + " Cores)"); // increment by 1 since bar starts from 0. 131 | 132 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 133 | 134 | public void onStopTrackingTouch(SeekBar seekBar) { 135 | } 136 | 137 | @Override 138 | public void onStartTrackingTouch(SeekBar seekBar) { 139 | } 140 | 141 | @Override 142 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 143 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads); 144 | textView.setText("Thread Count (" + Long.toString(mSeekBar.getProgress() + 1) + " Cores)"); 145 | } 146 | }); 147 | } 148 | 149 | private void setupOpIntensity(final View rootView) { 150 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_opIntensity); 151 | mSeekBar.setProgress(4); //fixme debug version 152 | 153 | TextView textView = rootView.findViewById(R.id.txtBox_opIntensity); 154 | textView.setText("Ops (" + Long.toString(1 << mSeekBar.getProgress()) + " FLOPS/byte)"); 155 | 156 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runRoofline); 157 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 158 | 159 | @Override 160 | public void onStopTrackingTouch(SeekBar seekBar) { 161 | toggle.setChecked(false); 162 | } 163 | 164 | @Override 165 | public void onStartTrackingTouch(SeekBar seekBar) { 166 | toggle.setChecked(false); 167 | } 168 | 169 | @Override 170 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 171 | TextView textView = rootView.findViewById(R.id.txtBox_opIntensity); 172 | textView.setText("Ops (" + Long.toString(1 << mSeekBar.getProgress()) + " FLOPS/byte)"); 173 | } 174 | }); 175 | } 176 | 177 | private void setupMemorySlider(final View rootView) { 178 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_threadMem); 179 | 180 | TextView textView = rootView.findViewById(R.id.txtBox_threadMem); 181 | textView.setText("Memory Alloc. (" + Long.toString(1 << mSeekBar.getProgress()) + " MB)"); 182 | 183 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runRoofline); 184 | 185 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 186 | 187 | @Override 188 | public void onStopTrackingTouch(SeekBar seekBar) { 189 | toggle.setChecked(false); 190 | } 191 | 192 | @Override 193 | public void onStartTrackingTouch(SeekBar seekBar) { 194 | toggle.setChecked(false); 195 | } 196 | 197 | @Override 198 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 199 | TextView textView = rootView.findViewById(R.id.txtBox_threadMem); 200 | textView.setText("Memory Alloc. (" + Long.toString(1 << mSeekBar.getProgress()) + " MB)"); 201 | } 202 | }); 203 | } 204 | 205 | private void setupSwitch(final View rootView) { 206 | final Switch hvxSwitch = rootView.findViewById(R.id.switch_hvxMode); 207 | 208 | hvxSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 209 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 210 | TextView textView = rootView.findViewById(R.id.txtBox_hvxSwitch); 211 | if (isChecked) { 212 | textView.setText("HVX Vectorization (Enabled)"); 213 | } else { 214 | textView.setText("HVX Vectorization (Disabled)"); 215 | } 216 | 217 | SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_maxThreads); 218 | int nCores = DSP_NumThreads(isChecked); 219 | mSeekBar.setMax(nCores - 1); // we start from 0, but we display +1 220 | mSeekBar.setProgress(nCores - 1); 221 | 222 | TextView textView2 = rootView.findViewById(R.id.txtBox_maxThreads); 223 | textView2.setText("Thread Count (" + Long.toString(mSeekBar.getProgress() + 1) + " Cores)"); // increment by 1 since bar starts from 0. 224 | } 225 | }); 226 | } 227 | 228 | @Override 229 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 230 | Bundle savedInstanceState) { 231 | View rootView = inflater.inflate(R.layout.fragment_dsp_roofline, container, false); 232 | 233 | drawGraph(rootView); 234 | 235 | setupMemorySlider(rootView); 236 | setupThreadCountSlider(rootView); 237 | setupOpIntensity(rootView); 238 | 239 | setupSwitch(rootView); 240 | 241 | setupButton(rootView); 242 | 243 | return rootView; 244 | } 245 | 246 | private void writeToSDFile(File dir, String filename, String ostr) { 247 | 248 | boolean success = true; 249 | if (!dir.exists()) { 250 | success = dir.mkdir(); // FIXME: if this fails, then you might have to request permission in app settings. 251 | } 252 | if (success) { 253 | Log.i(TAG, "Output directory created."); 254 | } else { 255 | Log.i(TAG, "Output directory NOT created!"); 256 | } 257 | 258 | File file = new File(dir, filename); 259 | 260 | try { 261 | FileOutputStream fos = new FileOutputStream(file); 262 | fos.write(ostr.getBytes()); 263 | fos.close(); 264 | } catch (FileNotFoundException e) { 265 | e.printStackTrace(); 266 | Log.i(TAG, "******* File not found. Did you" + 267 | " add a WRITE_EXTERNAL_STORAGE permission to the manifest?"); 268 | } catch (IOException e) { 269 | e.printStackTrace(); 270 | } 271 | Log.i(TAG, "\n\nFile written to " + file); 272 | } 273 | 274 | public void DSPRoofline(int nThreads, int maxMemory, int nFlops, boolean hvx) { 275 | DSPRooflineAsync rooflineATask = new DSPRooflineAsync(getActivity(), 276 | nThreads, maxMemory, nFlops, hvx); 277 | 278 | rooflineATask.execute(); 279 | } 280 | 281 | class DSPRooflineAsync extends AsyncTask { 282 | final int threads, mem, flops; 283 | final boolean hvx; 284 | private AsyncTask currTask = null; 285 | 286 | public DSPRooflineAsync(Activity activity, int nThreads, int threadMem, int maxFlops, boolean hvx) { 287 | super(); 288 | 289 | this.threads = nThreads; 290 | this.mem = threadMem; 291 | this.flops = maxFlops; 292 | this.hvx = hvx; 293 | 294 | gProcessDialog = new ProgressDialog(activity); 295 | } 296 | 297 | @Override 298 | protected void onPreExecute() { 299 | super.onPreExecute(); 300 | 301 | currTask = this; 302 | 303 | gProcessDialog.setMax(100); 304 | gProcessDialog.setProgress(0); 305 | gProcessDialog.setTitle("Roofline in progress"); 306 | gProcessDialog.setMessage("Starting up... "); 307 | gProcessDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 308 | gProcessDialog.setCancelable(false); 309 | gProcessDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { 310 | @Override 311 | public void onClick(DialogInterface dialog, int which) { 312 | dialog.dismiss(); 313 | currTask.cancel(true); 314 | } 315 | }); 316 | 317 | gProcessDialog.show(); 318 | } 319 | 320 | @Override 321 | protected String doInBackground(Void... params) { 322 | File root = android.os.Environment.getExternalStorageDirectory(); 323 | File odir = new File(root.getAbsolutePath() + "/" + gResultsDir + "/"); 324 | deleteDirectory(odir); 325 | Log.i(TAG, "Deleting the " + odir + " output directory."); 326 | 327 | // call some function that runs on the DSP 328 | DSP_Execute(mem, threads, flops, hvx); 329 | 330 | return "All done with AsyncTask!"; 331 | } 332 | 333 | @Override 334 | protected void onProgressUpdate(String... msg) { 335 | super.onProgressUpdate(msg); 336 | gProcessDialog.setProgress(Integer.parseInt(msg[0])); 337 | gProcessDialog.setMessage(msg[1]); 338 | } 339 | 340 | // onCancelled() is called when the async task is cancelled. 341 | @Override 342 | protected void onCancelled(String result) { 343 | } 344 | 345 | protected void onPostExecute(String param) { 346 | 347 | gProcessDialog.setMessage("Finished processing data."); 348 | 349 | if (gProcessDialog.isShowing()) { 350 | gProcessDialog.dismiss(); 351 | } 352 | } 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/gables/GPURoofline.java: -------------------------------------------------------------------------------- 1 | package com.google.gables; 2 | 3 | 4 | import android.app.Activity; 5 | import android.app.ProgressDialog; 6 | import android.content.DialogInterface; 7 | import android.content.res.AssetManager; 8 | import android.graphics.Bitmap; 9 | import android.graphics.BitmapFactory; 10 | import android.graphics.Color; 11 | import android.opengl.GLES31; 12 | import android.os.AsyncTask; 13 | import android.os.Bundle; 14 | import android.os.Environment; 15 | import android.support.v4.app.Fragment; 16 | import android.util.Log; 17 | import android.view.LayoutInflater; 18 | import android.view.View; 19 | import android.view.ViewGroup; 20 | import android.widget.CompoundButton; 21 | import android.widget.ImageView; 22 | import android.widget.SeekBar; 23 | import android.widget.TextView; 24 | import android.widget.ToggleButton; 25 | 26 | import com.jjoe64.graphview.GraphView; 27 | import com.jjoe64.graphview.GridLabelRenderer; 28 | import com.jjoe64.graphview.series.DataPoint; 29 | import com.jjoe64.graphview.series.LineGraphSeries; 30 | import com.synnapps.carouselview.CarouselView; 31 | import com.synnapps.carouselview.ImageListener; 32 | 33 | import java.io.File; 34 | import java.nio.IntBuffer; 35 | 36 | import gables.gables_processor.GablesPython; 37 | 38 | import static android.opengl.GLES31.GL_MAX_COMPUTE_WORK_GROUP_COUNT; 39 | import static android.opengl.GLES31.GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS; 40 | import static android.opengl.GLES31.GL_MAX_COMPUTE_WORK_GROUP_SIZE; 41 | import static com.google.gables.Roofline.GPU_Execute; 42 | import static com.google.gables.Roofline.GPU_Finish; 43 | import static com.google.gables.Roofline.GPU_Initialize; 44 | import static com.google.gables.Roofline.GPU_MaxWorkGroupCount; 45 | import static com.google.gables.Roofline.GPU_MaxWorkGroupSize; 46 | import static com.google.gables.Utils.ExtStoragePermissions_t.EXT_STORAGE_RW; 47 | import static com.google.gables.Utils.MiB; 48 | import static com.google.gables.Utils.deleteDirectory; 49 | import static com.google.gables.Utils.isStorageAvailable; 50 | import static com.google.gables.Utils.log; 51 | import static com.google.gables.Utils.writeToSDFile; 52 | 53 | public class GPURoofline extends Fragment { 54 | private static final String TAG = GPURoofline.class.getName(); 55 | 56 | private ProgressDialog gProcessDialog; 57 | private String gResultsDir = "GPURoofline"; 58 | private CarouselView slider; 59 | private View slidePrompt, edgeLogo; 60 | @Override 61 | public void onCreate(Bundle savedInstanceState) { 62 | super.onCreate(savedInstanceState); 63 | } 64 | 65 | // void drawGraph(View rootView) { 66 | // // Setup a graph view 67 | // GraphView graphview = rootView.findViewById(R.id.graph); 68 | // 69 | // // activate horizontal zooming and scrolling 70 | // graphview.getViewport().setScalable(false); 71 | // graphview.getViewport().setScrollable(true); 72 | // graphview.getViewport().setScalableY(false); 73 | // graphview.getViewport().setScrollableY(true); 74 | // 75 | // LineGraphSeries series = new LineGraphSeries<>(new DataPoint[]{ 76 | // new DataPoint(0, 1), 77 | // new DataPoint(1, 2), 78 | // new DataPoint(2, 3) 79 | // }); 80 | // 81 | // GridLabelRenderer gridLabel = graphview.getGridLabelRenderer(); 82 | // gridLabel.setHorizontalAxisTitle("Operational Intensity (FLOPS/byte)"); 83 | // gridLabel.setVerticalAxisTitle("Performance (GFLOPS)"); 84 | // 85 | // graphview.setTitle("GPU Roofline"); 86 | // 87 | // series.setTitle("GPU Roofline"); 88 | // series.setBackgroundColor(Color.GRAY); 89 | // series.setColor(Color.BLACK); 90 | // series.setDrawDataPoints(false); 91 | // series.setDataPointsRadius(10); 92 | // series.setThickness(7); 93 | // 94 | // graphview.addSeries(series); 95 | // 96 | // Log.d(TAG, "Finished drawing graphview for GPU Roofline"); 97 | // } 98 | 99 | void printOpenGLInfo() { //fixme, if this works i can get rid of the GPU JNI calls. 100 | IntBuffer work_grp_info = IntBuffer.allocate(3); 101 | 102 | // maximum global work group (total work in a dispatch) 103 | GLES31.glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_COUNT, work_grp_info); 104 | Log.i(TAG, "max global (total) work group size x:" + work_grp_info.get(0) 105 | + " y: " + work_grp_info.get(1) 106 | + " z: " + work_grp_info.get(2)); 107 | 108 | // maximum local work group (one shader's slice) 109 | GLES31.glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_SIZE, work_grp_info); 110 | Log.i(TAG, "max local (in one shader) work group sizes x:" + work_grp_info.get(0) 111 | + " y: " + work_grp_info.get(1) 112 | + " z: " + work_grp_info.get(2)); 113 | 114 | IntBuffer work_grp_invo = IntBuffer.allocate(1); 115 | // maximum compute shader invocations (x * y * z) 116 | GLES31.glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, work_grp_invo); 117 | Log.i(TAG, "max computer shader invocations: " + work_grp_invo.get(0)); 118 | } 119 | 120 | /** 121 | * Setup a button to trigger the activation of the roofline process 122 | * 123 | * @param rootView 124 | */ 125 | void setupButton(final View rootView) { 126 | // Register a callback on the toggle button to turn ON/OFF cpu activity 127 | final ToggleButton button = rootView.findViewById(R.id.toggle_runRoofline); 128 | button.setTextOn("Busy!"); 129 | button.setTextOff("Run"); 130 | button.setChecked(false); // set the current state of a toggle button 131 | 132 | button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 133 | 134 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 135 | 136 | if (isChecked) { // enabled 137 | SeekBar seekBar_maxWorkGroupCount = rootView.findViewById(R.id.seekBar_maxWorkGroupCount); 138 | SeekBar seekBar_maxWorkGroupSize = rootView.findViewById(R.id.seekBar_maxWorkGroupSize); 139 | SeekBar seekBar_opIntensity = rootView.findViewById(R.id.seekBar_opIntensity); 140 | SeekBar seekBar_memSize = rootView.findViewById(R.id.seekBar_maxMemory); 141 | 142 | int nGroups = 1 << seekBar_maxWorkGroupCount.getProgress(); // 2^maxGroups 143 | int nThreads = 1 << seekBar_maxWorkGroupSize.getProgress(); // 2^maxThreads 144 | int nFlops = 1 << seekBar_opIntensity.getProgress(); // 2^flops 145 | int memSize = (1 << seekBar_memSize.getProgress()) * MiB; // MB 146 | 147 | GPURooflineAsync(nGroups, nThreads, nFlops, memSize); 148 | 149 | button.setChecked(false); 150 | } 151 | } 152 | }); 153 | } 154 | 155 | public void GPURooflineAsync(int nGroups, int nThreads, int nFlops, int memSize) { 156 | GPURooflineAsyncTask rooflineATask = new GPURooflineAsyncTask(getActivity(), 157 | nGroups, nThreads, nFlops, memSize); 158 | rooflineATask.execute(); 159 | } 160 | 161 | private void setupMemAllocSlider(final View rootView) { 162 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_maxMemory); 163 | 164 | TextView textView = rootView.findViewById(R.id.textView_maxMemory); 165 | textView.setText("Memory Alloc. (" + Long.toString(1 << mSeekBar.getProgress()) + " MB)"); 166 | 167 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runRoofline); 168 | 169 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 170 | 171 | @Override 172 | public void onStopTrackingTouch(SeekBar seekBar) { 173 | toggle.setChecked(false); 174 | } 175 | 176 | @Override 177 | public void onStartTrackingTouch(SeekBar seekBar) { 178 | toggle.setChecked(false); 179 | } 180 | 181 | @Override 182 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 183 | TextView textView = rootView.findViewById(R.id.textView_maxMemory); 184 | textView.setText("Memory Alloc. (" + Long.toString(1 << mSeekBar.getProgress()) + " MB)"); 185 | } 186 | }); 187 | } 188 | 189 | private void setupOpIntensitySlider(final View rootView) { 190 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_opIntensity); 191 | 192 | mSeekBar.setProgress(mSeekBar.getMax()); //fixme: use setMin() but that's not available till API level 26 193 | 194 | TextView textView = rootView.findViewById(R.id.txtBox_opIntensity); 195 | textView.setText("Ops (" + Long.toString(1 << mSeekBar.getProgress()) + " FLOPS/byte)"); 196 | 197 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runRoofline); 198 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 199 | 200 | @Override 201 | public void onStopTrackingTouch(SeekBar seekBar) { 202 | toggle.setChecked(false); 203 | } 204 | 205 | @Override 206 | public void onStartTrackingTouch(SeekBar seekBar) { 207 | toggle.setChecked(false); 208 | } 209 | 210 | @Override 211 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 212 | TextView textView = rootView.findViewById(R.id.txtBox_opIntensity); 213 | textView.setText("Ops (" + Long.toString(1 << mSeekBar.getProgress()) + " FLOPS/byte)"); 214 | } 215 | }); 216 | } 217 | 218 | private void setupMaxWorkGroupSizeSlider(final View rootView) { 219 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_maxWorkGroupSize); 220 | 221 | // determine the maximum number of threads per work group 222 | GPU_Initialize(); 223 | int maxThreads = GPU_MaxWorkGroupSize(0); 224 | GPU_Finish(); 225 | 226 | mSeekBar.setMax((int) Math.ceil(log(maxThreads, 2))); 227 | mSeekBar.setProgress((int) Math.ceil(log(256, 2))); 228 | 229 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads); 230 | textView.setText("Work Group Size (" + Integer.toString(1 << mSeekBar.getProgress()) + ")"); // increment by 1 since bar starts from 0. 231 | 232 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runRoofline); 233 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 234 | 235 | @Override 236 | public void onStopTrackingTouch(SeekBar seekBar) { 237 | toggle.setChecked(false); 238 | } 239 | 240 | @Override 241 | public void onStartTrackingTouch(SeekBar seekBar) { 242 | toggle.setChecked(false); 243 | } 244 | 245 | @Override 246 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 247 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads); 248 | int maxThreads = (int) Math.pow(2, mSeekBar.getProgress()); 249 | textView.setText("Work Group Size (" + Integer.toString(maxThreads) + ")"); 250 | } 251 | }); 252 | } 253 | 254 | private void setupMaxWorkGroupCountSlider(final View rootView) { 255 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_maxWorkGroupCount); 256 | 257 | GPU_Initialize(); 258 | int maxGroups = GPU_MaxWorkGroupCount(0); 259 | GPU_Finish(); 260 | 261 | mSeekBar.setMax((int) Math.ceil(log(maxGroups, 2))); 262 | mSeekBar.setProgress((int) Math.ceil(log(1024, 2))); 263 | 264 | TextView textView = rootView.findViewById(R.id.txtBox_maxWorkGroups); 265 | textView.setText("Work Group Count (" + Integer.toString(1 << mSeekBar.getProgress()) + ")"); // increment by 1 since bar starts from 0. 266 | 267 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runRoofline); 268 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 269 | 270 | @Override 271 | public void onStopTrackingTouch(SeekBar seekBar) { 272 | toggle.setChecked(false); 273 | } 274 | 275 | @Override 276 | public void onStartTrackingTouch(SeekBar seekBar) { 277 | toggle.setChecked(false); 278 | } 279 | 280 | @Override 281 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 282 | TextView textView = rootView.findViewById(R.id.txtBox_maxWorkGroups); 283 | int maxGroups = (int) Math.pow(2, mSeekBar.getProgress()); 284 | textView.setText("Work Group Count (" + Integer.toString(maxGroups) + ")"); 285 | } 286 | }); 287 | } 288 | 289 | @Override 290 | public void onDestroyView() { 291 | super.onDestroyView(); 292 | Log.d(TAG, "onDestryView being called to terminate openGL."); 293 | } 294 | 295 | @Override 296 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 297 | Bundle savedInstanceState) { 298 | View rootView = inflater.inflate(R.layout.fragment_gpu_roofline, container, false); 299 | 300 | // drawGraph(rootView); 301 | 302 | setupMaxWorkGroupCountSlider(rootView); 303 | setupMaxWorkGroupSizeSlider(rootView); 304 | setupOpIntensitySlider(rootView); 305 | setupMemAllocSlider(rootView); 306 | 307 | setupButton(rootView); 308 | 309 | slider = rootView.findViewById(R.id.carouselView); 310 | slidePrompt = rootView.findViewById(R.id.swipe_prompt); 311 | edgeLogo = rootView.findViewById(R.id.edge_logo); 312 | return rootView; 313 | } 314 | 315 | private void setupSlider() { 316 | ImageListener imageListener = new ImageListener() { 317 | @Override 318 | public void setImageForPosition(int position, ImageView imageView) { 319 | String filename = ""; 320 | switch (position) { 321 | case 0: 322 | default: 323 | filename = "/sdcard/GPURoofline/roofline.png"; 324 | break; 325 | case 1: 326 | filename = "/sdcard/GPURoofline/bandwidth.png"; 327 | break; 328 | } 329 | displayGraph(filename, imageView); 330 | } 331 | }; 332 | slider.setImageListener(imageListener); 333 | slider.setPageCount(2); 334 | slider.setVisibility(View.VISIBLE); 335 | slidePrompt.setVisibility(View.VISIBLE); 336 | edgeLogo.setVisibility(View.GONE); 337 | } 338 | 339 | public void displayGraph(String filename, ImageView view) { 340 | File file = new File(filename); 341 | if (file.exists()) { 342 | Bitmap myBitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); 343 | view.setImageBitmap(myBitmap); 344 | } 345 | } 346 | class GPURooflineAsyncTask extends AsyncTask { 347 | final int threads, groups, flops, mem; 348 | AssetManager mgr; 349 | private AsyncTask currTask = null; 350 | 351 | 352 | public GPURooflineAsyncTask(Activity activity, int nGroups, int nThreads, int maxFlops, int memSize) { 353 | super(); 354 | 355 | this.groups = nGroups; 356 | this.threads = nThreads; 357 | this.flops = maxFlops; 358 | this.mgr = activity.getAssets(); 359 | this.mem = memSize; 360 | 361 | gProcessDialog = new ProgressDialog(activity); 362 | } 363 | 364 | @Override 365 | protected void onPreExecute() { 366 | super.onPreExecute(); 367 | 368 | currTask = this; 369 | 370 | gProcessDialog.setMax(100); 371 | gProcessDialog.setProgress(0); 372 | gProcessDialog.setTitle("Roofline in progress"); 373 | gProcessDialog.setMessage("Starting up... "); 374 | gProcessDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 375 | gProcessDialog.setCancelable(false); 376 | gProcessDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { 377 | @Override 378 | public void onClick(DialogInterface dialog, int which) { 379 | dialog.dismiss(); 380 | currTask.cancel(true); 381 | } 382 | }); 383 | 384 | gProcessDialog.show(); 385 | } 386 | 387 | @Override 388 | protected String doInBackground(Void... params) { 389 | File root = Environment.getExternalStorageDirectory(); 390 | File odir = new File(root.getAbsolutePath() + "/" + gResultsDir + "/"); 391 | deleteDirectory(odir); 392 | Log.i(TAG, "Deleting the " + odir + " output directory."); 393 | 394 | // storage for updating progress of the work to progress bar 395 | int progressCounter = 0; 396 | final int progressTotal = (log(flops, 2) + 1) 397 | * (log(threads, 2) + 1) 398 | * (log(groups, 2) + 1); 399 | 400 | int g = groups; 401 | int f = flops; 402 | int t = threads; 403 | GPU_Initialize(); 404 | if (g == 65536) { // fixme: make this more generic. 405 | g--; 406 | } 407 | String strCurrProgress = String.valueOf(Math.round(100.0 * progressCounter / progressTotal)); 408 | String message = "Running " + f + " FLOPS/byte with " + "<" + g + ", " + t + ">."; 409 | publishProgress(strCurrProgress, message); 410 | Log.i(TAG, message); 411 | 412 | String output = GPU_Execute(mgr, g, t, f, mem); 413 | Log.d(TAG, "Finished " + message); 414 | 415 | if (isStorageAvailable(EXT_STORAGE_RW)) { 416 | message = "Writing results to storage."; 417 | publishProgress(strCurrProgress, message); 418 | 419 | String ofilename = "groups-" + g + "_threads-" + t + "_flops-" + f + ".gables"; 420 | writeToSDFile(odir, ofilename, output); 421 | } else { 422 | Log.e(TAG, "Storage is not available for RW or is unavailable."); 423 | } 424 | // // we've made progress so update 425 | // progressCounter++; 426 | GPU_Finish(); 427 | Log.i(TAG, "Finished doing all the sweeps."); 428 | 429 | return "All done with AsyncTask!"; 430 | } 431 | 432 | @Override 433 | protected void onProgressUpdate(String... msg) { 434 | super.onProgressUpdate(msg); 435 | gProcessDialog.setProgress(Integer.parseInt(msg[0])); 436 | gProcessDialog.setMessage(msg[1]); 437 | } 438 | 439 | // onCancelled() is called when the async task is cancelled. 440 | @Override 441 | protected void onCancelled(String result) { 442 | } 443 | 444 | protected void onPostExecute(String param) { 445 | 446 | gProcessDialog.setMessage("Finished processing data."); 447 | 448 | new GablesPython().processGPURoofline(); 449 | setupSlider(); 450 | 451 | if (gProcessDialog.isShowing()) { 452 | gProcessDialog.dismiss(); 453 | } 454 | } 455 | } 456 | } 457 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/gables/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.google.gables; 2 | 3 | import android.Manifest; 4 | import android.content.Context; 5 | import android.content.pm.PackageManager; 6 | import android.os.Bundle; 7 | import android.support.annotation.NonNull; 8 | import android.support.annotation.Nullable; 9 | import android.support.design.widget.FloatingActionButton; 10 | import android.support.design.widget.Snackbar; 11 | import android.support.design.widget.TabLayout; 12 | import android.support.v4.app.ActivityCompat; 13 | import android.support.v4.app.Fragment; 14 | import android.support.v4.app.FragmentManager; 15 | import android.support.v4.app.FragmentPagerAdapter; 16 | import android.support.v4.content.ContextCompat; 17 | import android.support.v4.view.ViewPager; 18 | import android.support.v7.app.AppCompatActivity; 19 | import android.util.AttributeSet; 20 | import android.view.Menu; 21 | import android.view.MenuItem; 22 | import android.view.MotionEvent; 23 | import android.view.View; 24 | 25 | import com.google.gables.utils.NonSwipeViewPager; 26 | 27 | public class MainActivity extends AppCompatActivity { 28 | 29 | static final Integer WRITE_EXST = 0x3; 30 | static final Integer READ_EXST = 0x4; 31 | /** 32 | * The {@link android.support.v4.view.PagerAdapter} that will provide 33 | * fragments for each of the sections. We use a 34 | * {@link FragmentPagerAdapter} derivative, which will keep every 35 | * loaded fragment in memory. If this becomes too memory intensive, it 36 | * may be best to switch to a 37 | * {@link android.support.v4.app.FragmentStatePagerAdapter}. 38 | */ 39 | private SectionsPagerAdapter mSectionsPagerAdapter; 40 | /** 41 | * The {@link ViewPager} that will host the section contents. 42 | */ 43 | private NonSwipeViewPager mViewPager; 44 | 45 | @Override 46 | protected void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | setContentView(R.layout.activity_main); 49 | 50 | // Create the adapter that will return a fragment for each of the three 51 | // primary sections of the activity. 52 | mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); 53 | 54 | // Set up the ViewPager with the sections adapter. 55 | mViewPager = findViewById(R.id.container); 56 | mViewPager.setAdapter(mSectionsPagerAdapter); 57 | 58 | TabLayout tabLayout = findViewById(R.id.tabs); 59 | tabLayout.setupWithViewPager(mViewPager); 60 | 61 | askForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, WRITE_EXST); 62 | askForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, READ_EXST); 63 | } 64 | 65 | /** 66 | * This application stores data to the storage, and so we need to get access to that from the user. 67 | * 68 | * @param permission 69 | * @param requestCode 70 | */ 71 | private void askForPermission(String permission, Integer requestCode) { // FIXME Need to check onRequestPermission 72 | if (ContextCompat.checkSelfPermission(MainActivity.this, permission) != PackageManager.PERMISSION_GRANTED) { 73 | // Should we show an explanation? 74 | if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, permission)) { 75 | //This is called if user has denied the permission before 76 | //In this case I am just asking the permission again 77 | ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode); 78 | } else { 79 | ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, requestCode); 80 | } 81 | } 82 | } 83 | 84 | @Override 85 | public boolean onCreateOptionsMenu(Menu menu) { 86 | // Inflate the menu; this adds items to the action bar if it is present. 87 | getMenuInflater().inflate(R.menu.menu_main, menu); 88 | return true; 89 | } 90 | 91 | @Override 92 | public boolean onTouchEvent(MotionEvent event) { 93 | return false; 94 | } 95 | 96 | @Override 97 | public boolean onOptionsItemSelected(MenuItem item) { 98 | // Handle action bar item clicks here. The action bar will 99 | // automatically handle clicks on the Home/Up button, so long 100 | // as you specify a parent activity in AndroidManifest.xml. 101 | int id = item.getItemId(); 102 | 103 | //noinspection SimplifiableIfStatement 104 | if (id == R.id.action_settings) { 105 | return true; 106 | } 107 | 108 | return super.onOptionsItemSelected(item); 109 | } 110 | /** 111 | * A {@link FragmentPagerAdapter} that returns a fragment corresponding to 112 | * one of the sections/tabs/pages. 113 | */ 114 | public class SectionsPagerAdapter extends FragmentPagerAdapter { 115 | 116 | public SectionsPagerAdapter(FragmentManager fm) { 117 | super(fm); 118 | } 119 | 120 | @Override 121 | public Fragment getItem(int position) { 122 | // getItem is called to instantiate the fragment for the given page. 123 | // Return a PlaceholderFragment (defined as a static inner class below). 124 | switch (position) { 125 | // case 0: 126 | // DSPRoofline dspRoofline = new DSPRoofline(); 127 | // return dspRoofline; 128 | // case 1: 129 | // SOCRoofline socRoofline = new SOCRoofline(); 130 | // return socRoofline; 131 | case 0: 132 | CPURoofline cpuRoofline = new CPURoofline(); 133 | return cpuRoofline; 134 | case 1: 135 | GPURoofline gpuRoofline = new GPURoofline(); 136 | return gpuRoofline; 137 | default: 138 | return null; 139 | } 140 | } 141 | 142 | @Override 143 | public CharSequence getPageTitle(int position) { 144 | switch (position) { 145 | case 0: 146 | return "CPU Roofline"; 147 | case 1: 148 | return "GPU Roofline"; 149 | } 150 | return null; 151 | } 152 | 153 | @Override 154 | public int getCount() { 155 | return 2; 156 | } 157 | 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/gables/Roofline.java: -------------------------------------------------------------------------------- 1 | package com.google.gables; 2 | 3 | import android.content.res.AssetManager; 4 | import android.util.Log; 5 | 6 | public class Roofline { 7 | private static final String TAG = DSPRoofline.class.getName(); 8 | 9 | /* ================================================================================= */ 10 | /* Library providing functionality */ 11 | /* ================================================================================= */ 12 | static { 13 | String filename; 14 | 15 | filename = "roofline"; 16 | try { 17 | System.loadLibrary(filename); 18 | Log.i(TAG, filename + " successfully loaded!"); 19 | } catch (UnsatisfiedLinkError e) { 20 | Log.e(TAG, filename + " NOT successfully loaded: " + e); 21 | Log.e(TAG, "Terminating application because library not found."); 22 | android.os.Process.killProcess(android.os.Process.myPid()); 23 | } 24 | 25 | // filename = "gables"; 26 | // try { 27 | // System.loadLibrary(filename); 28 | // Log.i(TAG, filename + " successfully loaded!"); 29 | // } catch (UnsatisfiedLinkError e) { 30 | // Log.e(TAG, filename + " NOT successfully loaded: " + e); 31 | // Log.e(TAG, "Terminating application because library not found."); 32 | // android.os.Process.killProcess(android.os.Process.myPid()); 33 | // } 34 | } 35 | 36 | /* ================================================================================= */ 37 | /* GPU code */ 38 | /* ================================================================================= */ 39 | 40 | public static boolean GPU_Initialize() { 41 | return new Roofline().GPUInitOpenGL(); 42 | } 43 | 44 | public static boolean GPU_Finish() { 45 | return new Roofline().GPUFiniOpenGL(); 46 | } 47 | 48 | public static int GPU_MaxWorkGroupSize(int dim) { 49 | return new Roofline().GPUMaxWorkGroupSize(dim); 50 | } 51 | 52 | public static int GPU_MaxWorkGroupCount(int dim) { 53 | return new Roofline().GPUMaxWorkGroupCount(dim); 54 | } 55 | 56 | public static int GPU_MaxThreadInnovations(int dim) { 57 | return new Roofline().GPUMaxThreadInnovations(dim); 58 | } 59 | 60 | public static String GPU_Execute(AssetManager mgr, int nGroups, int nThreads, int nFlops, int memSize) { 61 | return new Roofline().GPUExecute(mgr, nGroups, nThreads, nFlops, memSize); 62 | } 63 | 64 | public static String CPU_Execute(int memTotal, int nThreads, int nFlops, boolean neon) { 65 | return new Roofline().CPUExecute(memTotal, nThreads, nFlops, neon); 66 | } 67 | 68 | public static String DSP_Execute(int memTotal, int nThreads, int nFlops, boolean hvx) { 69 | return new Roofline().DSPExecute(memTotal, nThreads, nFlops, hvx); 70 | } 71 | 72 | public static int DSP_NumThreads(boolean hvx) { 73 | return new Roofline().DSPNumThreads(hvx); 74 | } 75 | 76 | public static String SOC_Mixer(int memTotal, int nFlops, float cpuWorkFraction, int cpuThreads, int gpuWorkGroupSize, int gpuWorkItemSize) { 77 | return new Roofline().SOCMixer(memTotal, nFlops, cpuWorkFraction, cpuThreads, gpuWorkGroupSize, gpuWorkItemSize); 78 | } 79 | 80 | public static int Utils_SetEnvLibraryPath(String var, String path) { 81 | return new Roofline().UtilsSetEnvLibraryPath(var, path); 82 | } 83 | 84 | /* ================================================================================= */ 85 | /* GPU code */ 86 | /* ================================================================================= */ 87 | private native int GPUMaxWorkGroupSize(int dim); 88 | 89 | private native int GPUMaxWorkGroupCount(int dim); 90 | 91 | private native int GPUMaxThreadInnovations(int dim); 92 | 93 | private native boolean GPUInitOpenGL(); 94 | 95 | private native boolean GPUFiniOpenGL(); 96 | 97 | private native String GPUExecute(AssetManager mgr, int nGroups, int nThreads, int nFlops, int memSize); 98 | 99 | /* ================================================================================= */ 100 | /* CPU code */ 101 | /* ================================================================================= */ 102 | private native String CPUExecute(int maxMem, int nThreads, int nFlops, boolean neon); 103 | 104 | /* ================================================================================= */ 105 | /* DSP code */ 106 | /* ================================================================================= */ 107 | private native String DSPExecute(int maxMem, int nThreads, int nFlops, boolean hvx); 108 | 109 | private native int DSPNumThreads(boolean hvx); 110 | 111 | /* ================================================================================= */ 112 | /* SOC code */ 113 | /* ================================================================================= */ 114 | private native String SOCMixer(int memTotal, int nFlops, float cpuWorkFraction, int cpuThreads, int gpuWorkGroupSize, int gpuWorkItemSize); 115 | 116 | /* ================================================================================= */ 117 | /* Utils code 118 | /* ================================================================================= */ 119 | private native int UtilsSetEnvLibraryPath(String var, String path); 120 | } 121 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/gables/SOCRoofline.java: -------------------------------------------------------------------------------- 1 | package com.google.gables; 2 | 3 | import android.app.Activity; 4 | import android.app.ProgressDialog; 5 | import android.content.DialogInterface; 6 | import android.graphics.Color; 7 | import android.graphics.PorterDuff; 8 | import android.os.AsyncTask; 9 | import android.os.Bundle; 10 | import android.support.v4.app.Fragment; 11 | import android.util.Log; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.widget.CompoundButton; 16 | import android.widget.SeekBar; 17 | import android.widget.TextView; 18 | import android.widget.ToggleButton; 19 | 20 | import java.io.File; 21 | 22 | import static com.google.gables.Roofline.GPU_Finish; 23 | import static com.google.gables.Roofline.GPU_Initialize; 24 | import static com.google.gables.Roofline.GPU_MaxWorkGroupCount; 25 | import static com.google.gables.Roofline.GPU_MaxWorkGroupSize; 26 | import static com.google.gables.Roofline.SOC_Mixer; 27 | import static com.google.gables.Utils.ExtStoragePermissions_t.EXT_STORAGE_RW; 28 | import static com.google.gables.Utils.MiB; 29 | import static com.google.gables.Utils.deleteDirectory; 30 | import static com.google.gables.Utils.getNumberOfCores; 31 | import static com.google.gables.Utils.isStorageAvailable; 32 | import static com.google.gables.Utils.log; 33 | import static com.google.gables.Utils.writeToSDFile; 34 | 35 | public class SOCRoofline extends Fragment { 36 | private static final String TAG = SOCRoofline.class.getName(); 37 | 38 | private ProgressDialog gProcessDialog; 39 | private String gResultsDir = "SOCRoofline"; 40 | 41 | private void setupMemorySlider(final View rootView) { 42 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_totalMem); 43 | 44 | TextView textView = rootView.findViewById(R.id.txtBox_totalMem); 45 | textView.setText("Total Memory Alloc. (" + Long.toString(1 << mSeekBar.getProgress()) + " MB)"); 46 | 47 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runMixer); 48 | 49 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 50 | 51 | @Override 52 | public void onStopTrackingTouch(SeekBar seekBar) { 53 | toggle.setChecked(false); 54 | } 55 | 56 | @Override 57 | public void onStartTrackingTouch(SeekBar seekBar) { 58 | toggle.setChecked(false); 59 | } 60 | 61 | @Override 62 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 63 | TextView textView = rootView.findViewById(R.id.txtBox_totalMem); 64 | textView.setText("Total Memory Alloc. (" + Long.toString(1 << mSeekBar.getProgress()) + " MB)"); 65 | 66 | // need to update associated text 67 | setupWorkFractionSlider(rootView); 68 | } 69 | }); 70 | } 71 | 72 | private void setupMaxWorkGroupSizeSlider(final View rootView) { 73 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_maxGPUWorkgroupSize); 74 | 75 | // determine the maximum number of threads per work group 76 | GPU_Initialize(); 77 | int maxThreads = GPU_MaxWorkGroupCount(0); 78 | GPU_Finish(); 79 | 80 | mSeekBar.setMax((int) Math.ceil(log(maxThreads, 2))); 81 | 82 | mSeekBar.setProgress((int) Math.ceil(log(1024, 2))); //fixme: remove hardcode 83 | 84 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads6); 85 | maxThreads = (int) Math.pow(2, mSeekBar.getProgress()); 86 | textView.setText("GPU Work Group Size (" + Integer.toString(1 << mSeekBar.getProgress()) + ")"); // increment by 1 since bar starts from 0. 87 | 88 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runMixer); 89 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 90 | 91 | @Override 92 | public void onStopTrackingTouch(SeekBar seekBar) { 93 | toggle.setChecked(false); 94 | } 95 | 96 | @Override 97 | public void onStartTrackingTouch(SeekBar seekBar) { 98 | toggle.setChecked(false); 99 | } 100 | 101 | @Override 102 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 103 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads6); 104 | int currSize = (int) Math.pow(2, mSeekBar.getProgress()); 105 | textView.setText("GPU Work Group Size (" + Integer.toString(currSize) + ")"); 106 | } 107 | }); 108 | } 109 | 110 | private void setupMaxWorkGroupItemsSlider(final View rootView) { 111 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_maxGPUWorkItems); 112 | 113 | GPU_Initialize(); 114 | int maxGroups = GPU_MaxWorkGroupSize(0); 115 | GPU_Finish(); 116 | 117 | mSeekBar.setMax((int) Math.ceil(log(maxGroups, 2))); 118 | mSeekBar.setProgress((int) Math.ceil(log(256, 2))); 119 | 120 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads4); 121 | textView.setText("GPU Work Items (" + Integer.toString(1 << mSeekBar.getProgress()) + ")"); // increment by 1 since bar starts from 0. 122 | 123 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runMixer); 124 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 125 | 126 | @Override 127 | public void onStopTrackingTouch(SeekBar seekBar) { 128 | toggle.setChecked(false); 129 | } 130 | 131 | @Override 132 | public void onStartTrackingTouch(SeekBar seekBar) { 133 | toggle.setChecked(false); 134 | } 135 | 136 | @Override 137 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 138 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads4); 139 | int currSize = (int) Math.pow(2, mSeekBar.getProgress()); 140 | textView.setText("GPU Work Items (" + Integer.toString(currSize) + ")"); 141 | } 142 | }); 143 | } 144 | 145 | private void setupWorkFractionSlider(final View rootView) { 146 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_workFraction); 147 | final SeekBar seekBarTotalMem = rootView.findViewById(R.id.seekBar_totalMem); 148 | 149 | mSeekBar.getProgressDrawable().setColorFilter(Color.YELLOW, PorterDuff.Mode.MULTIPLY); 150 | mSeekBar.getThumb().setColorFilter(Color.LTGRAY, PorterDuff.Mode.MULTIPLY); 151 | 152 | final TextView cpuTextView = rootView.findViewById(R.id.txtBox_workFractionCPU); 153 | final TextView gpuTextView = rootView.findViewById(R.id.txtBox_workFractionGPU); 154 | final TextView wrkTextView = rootView.findViewById(R.id.txtBox_workFraction); 155 | 156 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runMixer); 157 | 158 | float cpu_frac = (float) mSeekBar.getProgress() / mSeekBar.getMax(); 159 | int cpu_mb = (int) ((1 << seekBarTotalMem.getProgress()) * cpu_frac); 160 | int gpu_mb = (int) ((1 << seekBarTotalMem.getProgress()) * (1.0 - cpu_frac)); 161 | 162 | cpuTextView.setText("CPU (" + cpu_mb + " MB)"); 163 | gpuTextView.setText("GPU (" + gpu_mb + " MB)"); 164 | wrkTextView.setText("Workload Offload Fraction (f): " + (int) ((1.0 - cpu_frac) * 100.0) + "%"); 165 | 166 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 167 | @Override 168 | public void onStopTrackingTouch(SeekBar seekBar) { 169 | toggle.setChecked(false); 170 | } 171 | 172 | @Override 173 | public void onStartTrackingTouch(SeekBar seekBar) { 174 | toggle.setChecked(false); 175 | } 176 | 177 | @Override 178 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 179 | float cpu_frac = (float) mSeekBar.getProgress() / mSeekBar.getMax(); 180 | int cpu_mb = (int) ((1 << seekBarTotalMem.getProgress()) * cpu_frac); 181 | int gpu_mb = (int) ((1 << seekBarTotalMem.getProgress()) * (1.0 - cpu_frac)); 182 | 183 | cpuTextView.setText("CPU (" + Long.toString(cpu_mb) + " MB)"); 184 | gpuTextView.setText("GPU (" + Long.toString(gpu_mb) + " MB)"); 185 | wrkTextView.setText("Workload Offload Fraction (f): " + String.format("%.2f", (1.0 - cpu_frac) * 100.0) + "%"); 186 | } 187 | }); 188 | } 189 | 190 | private void setupFlopsSlider(final View rootView) { 191 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_nFlops); 192 | 193 | TextView textView = rootView.findViewById(R.id.txtBox_nFlops); 194 | textView.setText("Ops (" + Long.toString(1 << mSeekBar.getProgress()) + " FLOPS/byte)"); // increment by 1 since bar starts from 0. 195 | 196 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runMixer); 197 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 198 | 199 | @Override 200 | public void onStopTrackingTouch(SeekBar seekBar) { 201 | toggle.setChecked(false); 202 | } 203 | 204 | @Override 205 | public void onStartTrackingTouch(SeekBar seekBar) { 206 | toggle.setChecked(false); 207 | } 208 | 209 | @Override 210 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 211 | TextView textView = rootView.findViewById(R.id.txtBox_nFlops); 212 | textView.setText("Ops (" + Long.toString(1 << mSeekBar.getProgress()) + " FLOPS/byte)"); 213 | } 214 | }); 215 | } 216 | 217 | private void setupMaxCPUThreadSlider(final View rootView) { 218 | final SeekBar mSeekBar = rootView.findViewById(R.id.seekBar_maxCPUThreads); 219 | 220 | int nCores = getNumberOfCores(); 221 | mSeekBar.setProgress(nCores); 222 | mSeekBar.setMax(nCores - 1); // we start from 0, but we display +1 223 | 224 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads2); 225 | textView.setText("CPU Thread Count (" + Long.toString(mSeekBar.getProgress() + 1) + " Cores)"); // increment by 1 since bar starts from 0. 226 | 227 | final ToggleButton toggle = rootView.findViewById(R.id.toggle_runMixer); 228 | mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 229 | 230 | @Override 231 | public void onStopTrackingTouch(SeekBar seekBar) { 232 | toggle.setChecked(false); 233 | } 234 | 235 | @Override 236 | public void onStartTrackingTouch(SeekBar seekBar) { 237 | toggle.setChecked(false); 238 | } 239 | 240 | @Override 241 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 242 | TextView textView = rootView.findViewById(R.id.txtBox_maxThreads2); 243 | textView.setText("CPU Thread Count (" + Long.toString(mSeekBar.getProgress() + 1) + " Cores)"); 244 | } 245 | }); 246 | } 247 | 248 | @Override 249 | public void onCreate(Bundle savedInstanceState) { 250 | super.onCreate(savedInstanceState); 251 | } 252 | 253 | @Override 254 | public void onDestroyView() { 255 | super.onDestroyView(); 256 | } 257 | 258 | @Override 259 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 260 | Bundle savedInstanceState) { 261 | View rootView = inflater.inflate(R.layout.fragment_mixer_roofline, container, false); 262 | 263 | setupMemorySlider(rootView); 264 | setupMaxCPUThreadSlider(rootView); 265 | setupMaxWorkGroupSizeSlider(rootView); 266 | setupMaxWorkGroupItemsSlider(rootView); 267 | setupWorkFractionSlider(rootView); 268 | setupFlopsSlider(rootView); 269 | setupButton(rootView); 270 | 271 | return rootView; 272 | } 273 | 274 | void setupButton(final View rootView) { 275 | final ToggleButton button = rootView.findViewById(R.id.toggle_runMixer); 276 | button.setChecked(false); // set the current state of a toggle button 277 | 278 | button.setTextOn("Busy!"); 279 | button.setTextOff("Run"); 280 | 281 | button.setChecked(false); 282 | 283 | button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 284 | 285 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 286 | 287 | if (isChecked) { // enabled 288 | SeekBar seekBar_split = rootView.findViewById(R.id.seekBar_workFraction); 289 | SeekBar seekBar_mem = rootView.findViewById(R.id.seekBar_totalMem); 290 | SeekBar seekBar_cpu = rootView.findViewById(R.id.seekBar_maxCPUThreads); 291 | SeekBar seekBar_gpuWorkGroupCount = rootView.findViewById(R.id.seekBar_maxGPUWorkgroupSize); 292 | SeekBar seekBar_gpuWorkGroupSize = rootView.findViewById(R.id.seekBar_maxGPUWorkItems); 293 | SeekBar seekBar_flops = rootView.findViewById(R.id.seekBar_nFlops); 294 | 295 | int memTotal = (1 << seekBar_mem.getProgress()) * MiB; // converting to bytes 296 | int gpuWorkGroupSize = 1 << seekBar_gpuWorkGroupCount.getProgress(); 297 | int gpuWorkItemSize = 1 << seekBar_gpuWorkGroupSize.getProgress(); 298 | int nFlops = 1 << seekBar_flops.getProgress(); 299 | int cpuThreads = seekBar_cpu.getProgress() + 1; // seekbar starts at 0 300 | 301 | float cpuWorkFraction = ((float) seekBar_split.getProgress() / seekBar_split.getMax()); 302 | 303 | Mixer(memTotal, cpuWorkFraction, nFlops, cpuThreads, gpuWorkGroupSize, gpuWorkItemSize); 304 | 305 | button.setChecked(false); 306 | } 307 | } 308 | }); 309 | } 310 | 311 | public void Mixer(int memTotal, float cpuWorkFraction, int nFlops, int cpuThreads, int gpuWorkGroupSize, int gpuWorkItemSize) { 312 | SOCRoofline.MixerAsync mixerATask = new SOCRoofline.MixerAsync(getActivity(), memTotal, cpuWorkFraction, nFlops, cpuThreads, gpuWorkGroupSize, gpuWorkItemSize); 313 | 314 | mixerATask.execute(); 315 | } 316 | 317 | class MixerAsync extends AsyncTask { 318 | final int memTotal, cpuThreads, gpuWorkGroupSize, gpuWorkItemSize, flops; 319 | final float cpuWorkFraction; 320 | 321 | private AsyncTask currTask = null; 322 | 323 | public MixerAsync(Activity activity, int memTotal, float cpuWorkFraction, int nFlops, int cpuThreads, int gpuWorkGroupSize, int gpuWorkItemSize) { 324 | super(); 325 | 326 | this.flops = nFlops; 327 | this.memTotal = memTotal; 328 | this.cpuWorkFraction = cpuWorkFraction; 329 | this.cpuThreads = cpuThreads; 330 | this.gpuWorkGroupSize = gpuWorkGroupSize; 331 | this.gpuWorkItemSize = gpuWorkItemSize; 332 | 333 | gProcessDialog = new ProgressDialog(activity); 334 | } 335 | 336 | @Override 337 | protected void onPreExecute() { 338 | super.onPreExecute(); 339 | 340 | currTask = this; 341 | 342 | gProcessDialog.setMax(100); 343 | gProcessDialog.setProgress(0); 344 | gProcessDialog.setTitle("Mixing in progress"); 345 | gProcessDialog.setMessage("Starting up... "); 346 | gProcessDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 347 | gProcessDialog.setCancelable(false); 348 | gProcessDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() { 349 | @Override 350 | public void onClick(DialogInterface dialog, int which) { 351 | dialog.dismiss(); 352 | currTask.cancel(true); 353 | } 354 | }); 355 | 356 | gProcessDialog.show(); 357 | } 358 | 359 | @Override 360 | protected String doInBackground(Void... params) { 361 | File root = android.os.Environment.getExternalStorageDirectory(); 362 | File odir = new File(root.getAbsolutePath() + "/" + gResultsDir + "/"); 363 | deleteDirectory(odir); 364 | Log.i(TAG, "Deleting the " + odir + " output directory."); 365 | 366 | String output = SOC_Mixer(memTotal, flops, cpuWorkFraction, cpuThreads, gpuWorkGroupSize, gpuWorkItemSize); 367 | Log.i(TAG, "Finished with mixing async task."); 368 | 369 | if (isStorageAvailable(EXT_STORAGE_RW)) { 370 | String ofilename = "mixer-CPUxGPU.txt"; 371 | writeToSDFile(odir, ofilename, output); 372 | } else { 373 | Log.e(TAG, "Storage is not available for RW or is unavailable."); 374 | } 375 | 376 | return "All done with MixerTask!"; 377 | } 378 | 379 | @Override 380 | protected void onProgressUpdate(String... msg) { 381 | super.onProgressUpdate(msg); 382 | gProcessDialog.setProgress(Integer.parseInt(msg[0])); 383 | gProcessDialog.setMessage(msg[1]); 384 | } 385 | 386 | // onCancelled() is called when the async task is cancelled. 387 | @Override 388 | protected void onCancelled(String result) { 389 | } 390 | 391 | protected void onPostExecute(String param) { 392 | 393 | gProcessDialog.setMessage("Finished processing data."); 394 | 395 | if (gProcessDialog.isShowing()) { 396 | gProcessDialog.dismiss(); 397 | } 398 | } 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/gables/Utils.java: -------------------------------------------------------------------------------- 1 | package com.google.gables; 2 | 3 | import android.os.Build; 4 | import android.os.Environment; 5 | import android.util.Log; 6 | 7 | import java.io.File; 8 | import java.io.FileNotFoundException; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | 12 | import static com.google.gables.Utils.ExtStoragePermissions_t.EXT_STORAGE_AV; 13 | import static com.google.gables.Utils.ExtStoragePermissions_t.EXT_STORAGE_RD; 14 | import static com.google.gables.Utils.ExtStoragePermissions_t.EXT_STORAGE_RW; 15 | 16 | public class Utils { 17 | 18 | public static final int KiB = 1024; 19 | public static final int MiB = KiB * 1024; 20 | public static final int GiB = MiB * 1024; 21 | private static final String TAG = CPURoofline.class.getName(); 22 | 23 | public static int getNumberOfCores() { 24 | int nCores = -1; 25 | if (Build.VERSION.SDK_INT >= 17) { 26 | nCores = Runtime.getRuntime().availableProcessors(); 27 | } else { 28 | throw new UnsupportedOperationException("Not yet implemented"); 29 | } 30 | 31 | return nCores; 32 | } 33 | 34 | public static int log(final int x, final int base) { 35 | return (int) (Math.log(x) / Math.log(base)); 36 | } 37 | 38 | public static Boolean isStorageAvailable(ExtStoragePermissions_t typeCheck) { 39 | boolean mExternalStorageAvailable = false; 40 | boolean mExternalStorageWriteable = false; 41 | String state = Environment.getExternalStorageState(); 42 | 43 | if (Environment.MEDIA_MOUNTED.equals(state)) { 44 | // Can read and write the media 45 | mExternalStorageAvailable = mExternalStorageWriteable = true; 46 | } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { 47 | // Can only read the media 48 | mExternalStorageAvailable = true; 49 | mExternalStorageWriteable = false; 50 | } else { 51 | // Can't read or write 52 | mExternalStorageAvailable = mExternalStorageWriteable = false; 53 | } 54 | Log.i("", "\n\nExternal Media: readable=" 55 | + mExternalStorageAvailable + " writable=" + mExternalStorageWriteable); 56 | 57 | if (typeCheck == EXT_STORAGE_RW) 58 | return (mExternalStorageAvailable && mExternalStorageWriteable); 59 | else if (typeCheck == EXT_STORAGE_RD) 60 | return (mExternalStorageAvailable && !mExternalStorageWriteable); 61 | else if (typeCheck == EXT_STORAGE_AV) 62 | return mExternalStorageAvailable; 63 | 64 | return false; 65 | } 66 | 67 | public static boolean deleteDirectory(File path) { 68 | if (path.exists()) { 69 | File[] files = path.listFiles(); 70 | if (files == null) { 71 | return true; 72 | } 73 | for (int i = 0; i < files.length; i++) { 74 | if (files[i].isDirectory()) { 75 | deleteDirectory(files[i]); 76 | } else { 77 | files[i].delete(); 78 | } 79 | } 80 | } 81 | return (path.delete()); 82 | } 83 | 84 | public static void writeToSDFile(File dir, String filename, String ostr) { 85 | 86 | boolean success = true; 87 | if (!dir.exists()) { 88 | success = dir.mkdir(); // FIXME: if this fails, then you might have to request permission in app settings. 89 | } 90 | if (success) { 91 | Log.i("writeToSDFile", "Output directory created."); 92 | } else { 93 | Log.i("writeToSDFile", "Output directory NOT created!"); 94 | } 95 | 96 | File file = new File(dir, filename); 97 | 98 | try { 99 | FileOutputStream fos = new FileOutputStream(file); 100 | fos.write(ostr.getBytes()); 101 | fos.close(); 102 | } catch (FileNotFoundException e) { 103 | e.printStackTrace(); 104 | Log.i("writeToSDFile", "******* File not found. Did you" + 105 | " add a WRITE_EXTERNAL_STORAGE permission to the manifest?"); 106 | } catch (IOException e) { 107 | e.printStackTrace(); 108 | } 109 | Log.i("writeToSDFile", "\n\nFile written to " + file); 110 | } 111 | 112 | public enum ExtStoragePermissions_t { 113 | EXT_STORAGE_RD, 114 | EXT_STORAGE_RW, 115 | EXT_STORAGE_AV 116 | } 117 | } -------------------------------------------------------------------------------- /app/src/main/java/com/google/gables/utils/NonSwipeViewPager.java: -------------------------------------------------------------------------------- 1 | package com.google.gables.utils; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.view.ViewPager; 7 | import android.util.AttributeSet; 8 | import android.view.MotionEvent; 9 | 10 | public class NonSwipeViewPager extends ViewPager { 11 | 12 | public NonSwipeViewPager(@NonNull Context context, @Nullable AttributeSet attrs) { 13 | super(context, attrs); 14 | } 15 | 16 | @Override 17 | public boolean onTouchEvent(MotionEvent ev) { 18 | return false; 19 | } 20 | 21 | @Override 22 | public boolean onInterceptTouchEvent(MotionEvent ev) { 23 | return false; 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/jni/roofline/CPUdriver.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "CPUdriver.hpp" 3 | #include "CPUkernel.hpp" 4 | 5 | double getTime() { 6 | double time; 7 | 8 | time = omp_get_wtime(); 9 | return time; 10 | } 11 | 12 | JNIEXPORT jstring JNICALL 13 | Java_com_google_gables_Roofline_CPUExecute(JNIEnv *env, jobject obj, 14 | jint memTotal, jint nThreads, jint nFlops, 15 | jboolean neon) { 16 | int id = 0; 17 | 18 | assert(nThreads >= 1); 19 | 20 | std::ostringstream results; 21 | results.str(""); 22 | uint64_t TSIZE = 100000000; 23 | 24 | double *__restrict__ buf = (double *) malloc(TSIZE); 25 | 26 | if (buf == NULL) { 27 | std::string str = "Error in CPU Roofline!"; 28 | return env->NewStringUTF("hi"); 29 | } 30 | 31 | omp_set_dynamic(0); // Explicitly disable dynamic teams 32 | omp_set_num_threads(nThreads); // Use nThreads for all consecutive parallel regions 33 | 34 | // based on the number of flops specified, pick the appropriate kernel to run 35 | void (*kernelPtr)(uint64_t, uint64_t, double *__restrict__, int *, int *); 36 | switch (nFlops) { 37 | case 1: 38 | kernelPtr = &kernel_1FLOPS; 39 | break; 40 | case 2: 41 | kernelPtr = &kernel_2FLOPS; 42 | break; 43 | case 4: 44 | kernelPtr = &kernel_4FLOPS; 45 | break; 46 | case 8: 47 | kernelPtr = &kernel_8FLOPS; 48 | break; 49 | case 16: 50 | kernelPtr = &kernel_16FLOPS; 51 | break; 52 | case 32: 53 | kernelPtr = &kernel_32FLOPS; 54 | break; 55 | case 64: 56 | kernelPtr = &kernel_64FLOPS; 57 | break; 58 | case 128: 59 | kernelPtr = &kernel_128FLOPS; 60 | break; 61 | case 256: 62 | kernelPtr = &kernel_256FLOPS; 63 | break; 64 | case 512: 65 | kernelPtr = &kernel_512FLOPS; 66 | break; 67 | case 1024: 68 | kernelPtr = &kernel_1024FLOPS; 69 | break; 70 | default: 71 | assert (false); 72 | } 73 | 74 | #pragma omp parallel private(id) proc_bind(spread) 75 | { 76 | id = omp_get_thread_num(); 77 | 78 | // determine thread offsets 79 | uint64_t nsize = TSIZE / nThreads; 80 | nsize = nsize & (~(32 - 1)); 81 | nsize = nsize / sizeof(double); 82 | uint64_t nid = nsize * id; 83 | 84 | // initialize small chunck of buffer within each thread 85 | initialize(nsize, &buf[nid], 1.0); 86 | 87 | uint64_t nNew = 0; 88 | for (uint64_t n = 1; n <= nsize;) { 89 | uint64_t ntrials = nsize / n; 90 | if (ntrials < 1) 91 | ntrials = 1; 92 | 93 | for (uint64_t t = 1; t <= ntrials; t = t * 2) { // working set - ntrials 94 | double startTime, endTime; 95 | int bytes_per_elem, mem_accesses_per_elem; 96 | 97 | #pragma omp barrier 98 | 99 | if (id == 0) { 100 | startTime = getTime(); 101 | } 102 | 103 | kernelPtr(n, t, &buf[nid], &bytes_per_elem, &mem_accesses_per_elem); 104 | 105 | #pragma omp barrier 106 | 107 | #pragma omp master 108 | if (id == 0) { 109 | endTime = getTime(); 110 | double seconds = (double) (endTime - startTime); 111 | uint64_t working_set_size = n * nThreads; 112 | uint64_t total_bytes = 113 | t * working_set_size * bytes_per_elem * mem_accesses_per_elem; 114 | uint64_t total_flops = t * working_set_size * nFlops; 115 | 116 | results << std::right << std::setw(12) << working_set_size * bytes_per_elem 117 | << std::right << std::setw(12) << t 118 | << std::right << std::setw(15) << seconds * 1000000 119 | << std::right << std::setw(12) << total_bytes 120 | << std::right << std::setw(12) << total_flops 121 | << std::endl; 122 | } 123 | } 124 | 125 | nNew = 1.1 * n; 126 | if (nNew == n) { 127 | nNew = n + 1; 128 | } 129 | 130 | n = nNew; 131 | } // working set - nsize 132 | 133 | } // parallel region 134 | 135 | free(buf); 136 | 137 | results << std::endl; 138 | results << "META_DATA" << std::endl; 139 | results << "FLOPS " << nFlops << std::endl; 140 | results << "OPENMP_THREADS " << nThreads << std::endl; 141 | return env->NewStringUTF(results.str().c_str()); 142 | } 143 | -------------------------------------------------------------------------------- /app/src/main/jni/roofline/CPUkernel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "rep.hpp" 6 | #include "CPUkernel.hpp" 7 | 8 | void initialize(uint64_t nsize, 9 | double *__restrict__ a, 10 | double value) { 11 | uint64_t i; 12 | for (i = 0; i < nsize; ++i) { 13 | a[i] = value; 14 | } 15 | } 16 | 17 | void kernel_1FLOPS(uint64_t nsize, 18 | uint64_t ntrials, 19 | double *__restrict__ A, 20 | int *bytes_per_elem, 21 | int *mem_accesses_per_elem) { 22 | *bytes_per_elem = sizeof(*A); 23 | *mem_accesses_per_elem = 2; 24 | 25 | double alpha = 0.5; 26 | uint64_t i, j; 27 | for (j = 0; j < ntrials; ++j) { 28 | for (i = 0; i < nsize; ++i) { 29 | double beta = 0.8; 30 | KERNEL1(beta, A[i], alpha); 31 | A[i] = beta; 32 | } 33 | alpha = alpha * (1 - 1e-8); 34 | } 35 | } 36 | 37 | void kernel_2FLOPS(uint64_t nsize, 38 | uint64_t ntrials, 39 | double *__restrict__ A, 40 | int *bytes_per_elem, 41 | int *mem_accesses_per_elem) { 42 | *bytes_per_elem = sizeof(*A); 43 | *mem_accesses_per_elem = 2; 44 | 45 | double alpha = 0.5; 46 | uint64_t i, j; 47 | for (j = 0; j < ntrials; ++j) { 48 | for (i = 0; i < nsize; ++i) { 49 | double beta = 0.8; 50 | KERNEL2(beta, A[i], alpha); 51 | A[i] = beta; 52 | } 53 | alpha = alpha * (1 - 1e-8); 54 | } 55 | } 56 | 57 | void kernel_4FLOPS(uint64_t nsize, 58 | uint64_t ntrials, 59 | double *__restrict__ A, 60 | int *bytes_per_elem, 61 | int *mem_accesses_per_elem) { 62 | *bytes_per_elem = sizeof(*A); 63 | *mem_accesses_per_elem = 2; 64 | 65 | double alpha = 0.5; 66 | uint64_t i, j; 67 | for (j = 0; j < ntrials; ++j) { 68 | for (i = 0; i < nsize; ++i) { 69 | double beta = 0.8; 70 | REP2(KERNEL2(beta, A[i], alpha)); 71 | A[i] = beta; 72 | } 73 | alpha = alpha * (1 - 1e-8); 74 | } 75 | } 76 | 77 | void kernel_8FLOPS(uint64_t nsize, 78 | uint64_t ntrials, 79 | double *__restrict__ A, 80 | int *bytes_per_elem, 81 | int *mem_accesses_per_elem) { 82 | *bytes_per_elem = sizeof(*A); 83 | *mem_accesses_per_elem = 2; 84 | 85 | double alpha = 0.5; 86 | uint64_t i, j; 87 | for (j = 0; j < ntrials; ++j) { 88 | for (i = 0; i < nsize; ++i) { 89 | double beta = 0.8; 90 | REP4(KERNEL2(beta, A[i], alpha)); 91 | A[i] = beta; 92 | } 93 | alpha = alpha * (1 - 1e-8); 94 | } 95 | } 96 | 97 | void kernel_16FLOPS(uint64_t nsize, 98 | uint64_t ntrials, 99 | double *__restrict__ A, 100 | int *bytes_per_elem, 101 | int *mem_accesses_per_elem) { 102 | *bytes_per_elem = sizeof(*A); 103 | *mem_accesses_per_elem = 2; 104 | 105 | double alpha = 0.5; 106 | uint64_t i, j; 107 | for (j = 0; j < ntrials; ++j) { 108 | for (i = 0; i < nsize; ++i) { 109 | double beta = 0.8; 110 | REP8(KERNEL2(beta, A[i], alpha)); 111 | A[i] = beta; 112 | } 113 | alpha = alpha * (1 - 1e-8); 114 | } 115 | } 116 | 117 | void kernel_32FLOPS(uint64_t nsize, 118 | uint64_t ntrials, 119 | double *__restrict__ A, 120 | int *bytes_per_elem, 121 | int *mem_accesses_per_elem) { 122 | *bytes_per_elem = sizeof(*A); 123 | *mem_accesses_per_elem = 2; 124 | 125 | double alpha = 0.5; 126 | uint64_t i, j; 127 | for (j = 0; j < ntrials; ++j) { 128 | for (i = 0; i < nsize; ++i) { 129 | double beta = 0.8; 130 | REP16(KERNEL2(beta, A[i], alpha)); 131 | A[i] = beta; 132 | } 133 | alpha = alpha * (1 - 1e-8); 134 | } 135 | } 136 | 137 | void kernel_64FLOPS(uint64_t nsize, 138 | uint64_t ntrials, 139 | double *__restrict__ A, 140 | int *bytes_per_elem, 141 | int *mem_accesses_per_elem) { 142 | *bytes_per_elem = sizeof(*A); 143 | *mem_accesses_per_elem = 2; 144 | 145 | double alpha = 0.5; 146 | uint64_t i, j; 147 | for (j = 0; j < ntrials; ++j) { 148 | for (i = 0; i < nsize; ++i) { 149 | double beta = 0.8; 150 | REP32(KERNEL2(beta, A[i], alpha)); 151 | A[i] = beta; 152 | } 153 | alpha = alpha * (1 - 1e-8); 154 | } 155 | } 156 | 157 | void kernel_128FLOPS(uint64_t nsize, 158 | uint64_t ntrials, 159 | double *__restrict__ A, 160 | int *bytes_per_elem, 161 | int *mem_accesses_per_elem) { 162 | *bytes_per_elem = sizeof(*A); 163 | *mem_accesses_per_elem = 2; 164 | 165 | double alpha = 0.5; 166 | uint64_t i, j; 167 | for (j = 0; j < ntrials; ++j) { 168 | for (i = 0; i < nsize; ++i) { 169 | double beta = 0.8; 170 | REP64(KERNEL2(beta, A[i], alpha)); 171 | A[i] = beta; 172 | } 173 | alpha = alpha * (1 - 1e-8); 174 | } 175 | } 176 | 177 | void kernel_256FLOPS(uint64_t nsize, 178 | uint64_t ntrials, 179 | double *__restrict__ A, 180 | int *bytes_per_elem, 181 | int *mem_accesses_per_elem) { 182 | *bytes_per_elem = sizeof(*A); 183 | *mem_accesses_per_elem = 2; 184 | 185 | double alpha = 0.5; 186 | uint64_t i, j; 187 | for (j = 0; j < ntrials; ++j) { 188 | for (i = 0; i < nsize; ++i) { 189 | double beta = 0.8; 190 | REP128(KERNEL2(beta, A[i], alpha)); 191 | A[i] = beta; 192 | } 193 | alpha = alpha * (1 - 1e-8); 194 | } 195 | } 196 | 197 | void kernel_512FLOPS(uint64_t nsize, 198 | uint64_t ntrials, 199 | double *__restrict__ A, 200 | int *bytes_per_elem, 201 | int *mem_accesses_per_elem) { 202 | *bytes_per_elem = sizeof(*A); 203 | *mem_accesses_per_elem = 2; 204 | 205 | double alpha = 0.5; 206 | uint64_t i, j; 207 | for (j = 0; j < ntrials; ++j) { 208 | for (i = 0; i < nsize; ++i) { 209 | double beta = 0.8; 210 | REP256(KERNEL2(beta, A[i], alpha)); 211 | A[i] = beta; 212 | } 213 | alpha = alpha * (1 - 1e-8); 214 | } 215 | } 216 | 217 | void kernel_1024FLOPS(uint64_t nsize, 218 | uint64_t ntrials, 219 | double *__restrict__ A, 220 | int *bytes_per_elem, 221 | int *mem_accesses_per_elem) { 222 | *bytes_per_elem = sizeof(*A); 223 | *mem_accesses_per_elem = 2; 224 | 225 | double alpha = 0.5; 226 | uint64_t i, j; 227 | for (j = 0; j < ntrials; ++j) { 228 | for (i = 0; i < nsize; ++i) { 229 | double beta = 0.8; 230 | REP512(KERNEL2(beta, A[i], alpha)); 231 | A[i] = beta; 232 | } 233 | alpha = alpha * (1 - 1e-8); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /app/src/main/jni/roofline/Utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Utils.hpp" 8 | 9 | double getTime() { 10 | double time; 11 | 12 | struct timeval tm; 13 | gettimeofday(&tm, NULL); 14 | time = tm.tv_sec + (tm.tv_usec / 1000000.0); 15 | 16 | return time; 17 | } 18 | 19 | double getOMPTime() { 20 | double time; 21 | 22 | time = omp_get_wtime(); 23 | return time; 24 | } 25 | 26 | //void initialize(uint64_t nsize, 27 | // double *__restrict__ A, 28 | // double value) { 29 | // uint64_t i; 30 | // for (i = 0; i < nsize; ++i) { 31 | // A[i] = value; 32 | // } 33 | //} 34 | 35 | JNIEXPORT jint JNICALL 36 | Java_com_google_gables_Roofline_UtilsSetEnvLibraryPath(JNIEnv *env, jobject thiz, 37 | jstring envVar, 38 | jstring envPath) { 39 | 40 | const char *nativeEnvVar = env->GetStringUTFChars(envVar, JNI_FALSE); 41 | const char *nativeEnvPath = env->GetStringUTFChars(envPath, JNI_FALSE); 42 | 43 | std::stringstream path; 44 | path << nativeEnvPath << ";/system/lib/rfsa/adsp;/system/vendor/lib/rfsa/adsp;/dsp"; 45 | 46 | int ret = setenv(nativeEnvVar, path.str().c_str(), 1 /*override*/) == 0; 47 | 48 | env->ReleaseStringUTFChars(envVar, nativeEnvVar); 49 | env->ReleaseStringUTFChars(envPath, nativeEnvPath); 50 | 51 | return ret; 52 | } -------------------------------------------------------------------------------- /app/src/main/jni/roofline/inc/CPUdriver.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ERT_DRIVER_H 2 | #define ERT_DRIVER_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include 18 | 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | JNIEXPORT jstring JNICALL 25 | Java_com_google_gables_Roofline_CPUExecute(JNIEnv *env, 26 | jobject obj, 27 | jint memTotal, 28 | jint nThreads, 29 | jint nFlops, 30 | jboolean neon); 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /app/src/main/jni/roofline/inc/CPUkernel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KERNEL1_H 2 | #define KERNEL1_H 3 | 4 | #define KERNEL1(a, b, c) ((a) = (b) + (c)) 5 | #define KERNEL2(a, b, c) ((a) = (a)*(b) + (c)) 6 | 7 | #include "rep.hpp" 8 | 9 | void initialize(uint64_t nsize, 10 | double *__restrict__ array, 11 | double value); 12 | 13 | void kernel(uint64_t nsize, 14 | uint64_t ntrials, 15 | double *__restrict__ array, 16 | int *bytes_per_elem, 17 | int *mem_accesses_per_elem); 18 | 19 | void kernel_1FLOPS(uint64_t nsize, 20 | uint64_t ntrials, 21 | double *__restrict__ array, 22 | int *bytes_per_elem, 23 | int *mem_accesses_per_elem); 24 | 25 | void kernel_2FLOPS(uint64_t nsize, 26 | uint64_t ntrials, 27 | double *__restrict__ array, 28 | int *bytes_per_elem, 29 | int *mem_accesses_per_elem); 30 | 31 | void kernel_4FLOPS(uint64_t nsize, 32 | uint64_t ntrials, 33 | double *__restrict__ array, 34 | int *bytes_per_elem, 35 | int *mem_accesses_per_elem); 36 | 37 | void kernel_8FLOPS(uint64_t nsize, 38 | uint64_t ntrials, 39 | double *__restrict__ array, 40 | int *bytes_per_elem, 41 | int *mem_accesses_per_elem); 42 | 43 | void kernel_16FLOPS(uint64_t nsize, 44 | uint64_t ntrials, 45 | double *__restrict__ array, 46 | int *bytes_per_elem, 47 | int *mem_accesses_per_elem); 48 | 49 | void kernel_32FLOPS(uint64_t nsize, 50 | uint64_t ntrials, 51 | double *__restrict__ array, 52 | int *bytes_per_elem, 53 | int *mem_accesses_per_elem); 54 | 55 | void kernel_64FLOPS(uint64_t nsize, 56 | uint64_t ntrials, 57 | double *__restrict__ array, 58 | int *bytes_per_elem, 59 | int *mem_accesses_per_elem); 60 | 61 | void kernel_128FLOPS(uint64_t nsize, 62 | uint64_t ntrials, 63 | double *__restrict__ array, 64 | int *bytes_per_elem, 65 | int *mem_accesses_per_elem); 66 | 67 | void kernel_256FLOPS(uint64_t nsize, 68 | uint64_t ntrials, 69 | double *__restrict__ array, 70 | int *bytes_per_elem, 71 | int *mem_accesses_per_elem); 72 | 73 | void kernel_512FLOPS(uint64_t nsize, 74 | uint64_t ntrials, 75 | double *__restrict__ array, 76 | int *bytes_per_elem, 77 | int *mem_accesses_per_elem); 78 | 79 | void kernel_1024FLOPS(uint64_t nsize, 80 | uint64_t ntrials, 81 | double *__restrict__ array, 82 | int *bytes_per_elem, 83 | int *mem_accesses_per_elem); 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /app/src/main/jni/roofline/inc/GPUdriver.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GABLES_GPUDRIVER_H 2 | #define GABLES_GPUDRIVER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | //#include "Utils.hpp" 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | typedef GLuint GPUKernelPtr; 31 | 32 | bool GPUInitializeOpenGL(); 33 | bool GPUShutdownOpenGL(); 34 | 35 | GPUKernelPtr GPUBuildKernel(int wgThreads, int nFlops); 36 | void GPUConfigureSSBO(uint total_size, uint ntrials); 37 | void GPULaunchKernel(GPUKernelPtr computeKernel, uint wgSize); 38 | 39 | JNIEXPORT jboolean JNICALL 40 | Java_com_google_gables_Roofline_GPUInitOpenGL(JNIEnv *env, jobject thiz); 41 | 42 | JNIEXPORT jboolean JNICALL 43 | Java_com_google_gables_Roofline_GPUFiniOpenGL(JNIEnv *env, jobject thiz); 44 | 45 | JNIEXPORT jint JNICALL 46 | Java_com_google_gables_Roofline_GPUMaxWorkGroupCount(JNIEnv *env, jobject thiz, jint dim); 47 | 48 | JNIEXPORT jint JNICALL 49 | Java_com_google_gables_Roofline_GPUMaxWorkGroupSize(JNIEnv *env, jobject thiz, jint dim); 50 | 51 | JNIEXPORT jint JNICALL 52 | Java_com_google_gables_Roofline_GPUMaxThreadInnovations(JNIEnv *env, jobject thiz, jint dim); 53 | 54 | JNIEXPORT jstring JNICALL 55 | Java_com_google_gables_Roofline_GPUExecute(JNIEnv *env, jobject thiz, jobject assetManager, 56 | jint nGroups, jint nThreads, jint nFlops, jint memTotal); 57 | 58 | JNIEXPORT jstring JNICALL 59 | Java_com_google_gables_Roofline_GPUTest(JNIEnv *env, jobject thiz); 60 | 61 | #ifdef __cplusplus 62 | } 63 | #endif 64 | 65 | #endif //GABLES_GPUDRIVER_H 66 | -------------------------------------------------------------------------------- /app/src/main/jni/roofline/inc/Utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GABLES_UTILS_HPP 2 | #define GABLES_UTILS_HPP 3 | 4 | #define KiB (1024) 5 | #define MiB (KiB * KiB) 6 | #define GiB (MiB * KiB) 7 | 8 | #define ALIGN (32) 9 | 10 | #define GFLOPS (1000000000) 11 | 12 | #define LOG_TAG ("JNI") 13 | 14 | #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args) 15 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args) 16 | #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args) 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | double getTime(); 23 | 24 | double getOMPTime(); 25 | 26 | //void initialize(uint64_t nsize, 27 | // float *__restrict__ array, 28 | // float value); 29 | 30 | JNIEXPORT jint JNICALL 31 | Java_com_google_gables_Roofline_UtilsSetEnvLibraryPath(JNIEnv *env, jobject thiz, 32 | jstring envVar, 33 | jstring envPath); 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | #endif //GABLES_UTILS_HPP -------------------------------------------------------------------------------- /app/src/main/jni/roofline/inc/rep.hpp: -------------------------------------------------------------------------------- 1 | #ifndef REP_H 2 | #define REP_H 3 | 4 | #define REP2(S) S ; S 5 | #define REP4(S) REP2(S); REP2(S) 6 | #define REP8(S) REP4(S); REP4(S) 7 | #define REP16(S) REP8(S); REP8(S) 8 | #define REP32(S) REP16(S); REP16(S) 9 | #define REP64(S) REP32(S); REP32(S) 10 | #define REP128(S) REP64(S); REP64(S) 11 | #define REP256(S) REP128(S); REP128(S) 12 | #define REP512(S) REP256(S); REP256(S) 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /app/src/main/python/gables/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/python/gables/__init__.py -------------------------------------------------------------------------------- /app/src/main/python/gables/gables_processor.py: -------------------------------------------------------------------------------- 1 | from java import constructor, method, static_proxy, jint, jarray, jfloat, jvoid 2 | from java.lang import String 3 | from android.os import Environment 4 | import os 5 | import numpy as np 6 | import math 7 | import matplotlib.pyplot as plt 8 | import matplotlib 9 | import statistics 10 | 11 | class GablesPython(static_proxy()): 12 | @constructor([]) 13 | def __init__(self): 14 | pass 15 | 16 | @method(jvoid, []) 17 | def processCPURoofline(self): 18 | try: 19 | os.chdir(os.path.join(str(Environment.getExternalStorageDirectory()), "CPURoofline/")) 20 | except: 21 | return None 22 | summaries = get_test_summaries() 23 | generate_roofline_plot(summaries) 24 | generate_bandwidth_plot(get_best_summary(summaries)) 25 | print("Plotting complete") 26 | 27 | @method(jvoid, []) 28 | def processGPURoofline(self): 29 | try: 30 | os.chdir(os.path.join(str(Environment.getExternalStorageDirectory()), "GPURoofline/")) 31 | except: 32 | return None 33 | summaries = get_test_summaries() 34 | generate_roofline_plot(summaries) 35 | generate_bandwidth_plot(get_best_summary(summaries)) 36 | print("Plotting complete") 37 | 38 | 39 | def smooth(y): 40 | ys = y[:] 41 | 42 | d = 0 43 | 44 | for i in range(len(ys)): 45 | num = min(len(ys),i+d+1) - max(0,i-d) 46 | total = sum(ys[max(0,i-d):min(len(ys),i+d+1)]) 47 | ys[i] = total/float(num) 48 | 49 | return ys 50 | 51 | def grouper(iterable): 52 | prev = None 53 | group = [] 54 | for item in iterable: 55 | if not prev or item - prev <= 2: 56 | group.append(item) 57 | else: 58 | yield group 59 | group = [item] 60 | prev = item 61 | if group: 62 | yield group 63 | 64 | def calculate_bandwidths(summary): 65 | max_band = max(summary.band) 66 | num_buckets = 10000 67 | threshold = 1.05 68 | buckets = [0] * num_buckets 69 | bucket_values = [0] * num_buckets 70 | band = summary.band[summary.band.index(max_band):] 71 | band = smooth(band) 72 | for i in range(0, num_buckets): 73 | value = (max_band / num_buckets) * i 74 | bucket_min = value/threshold 75 | bucket_max = value * threshold 76 | for b in band: 77 | if b > bucket_min and b < bucket_max: 78 | buckets[i] += 1 79 | bucket_values[i] = b 80 | 81 | band_list = [max_band] 82 | maxc = -1 83 | maxi = -1 84 | 85 | # Find all buckets with a large number of values 86 | for i in range(num_buckets-3,1,-1): 87 | if buckets[i] > 20: 88 | band_list.append(bucket_values[i]) 89 | 90 | # Do some simple clustering 91 | THRESHOLD = 0.25 92 | prev = None 93 | final_band_list = [] 94 | 95 | print("GROUPER BAND_LIST") 96 | grouped_dict = dict(enumerate(grouper(reversed(band_list)), 1)) 97 | for key in grouped_dict.keys(): 98 | final_band_list.append(statistics.mean(grouped_dict[key])) 99 | print(final_band_list) 100 | 101 | try: 102 | summary.max_dram = final_band_list[0] 103 | summary.max_l1 = final_band_list[1] 104 | summary.max_l2 = final_band_list[2] 105 | except: 106 | pass 107 | 108 | def calculate_weight(summary): 109 | weight = 0 110 | for i in range(0,len(summary.x)-1): 111 | x1 = math.log(summary.x[i]) 112 | y1 = summary.band[i] 113 | 114 | x2 = math.log(summary.x[i+1]) 115 | y2 = summary.band[i+1] 116 | 117 | weight += (y1+y2)/2.0 * (x2-x1) 118 | summary.weight = weight 119 | 120 | class RAW_INDEX: 121 | working_set_size = 0 122 | num_trials = 1 123 | time = 2 124 | bytes = 3 125 | flops=4 126 | 127 | class TestSummary(object): 128 | def __init__(self): 129 | self.max_gflops = -math.inf 130 | self.weight = None 131 | self.max_l1 = None 132 | self.max_l2 = None 133 | self.max_dram = None 134 | self.flops = None 135 | self.threads = None 136 | self.x = [] 137 | self.band = [] 138 | self.gflops = [] 139 | 140 | def get_test_summaries(): 141 | summaries = [] 142 | # Parse raw test data into list of test summaries 143 | for filename in os.listdir(): 144 | if filename.endswith(".png"): 145 | continue 146 | summary = TestSummary() 147 | with open(os.path.join(os.getcwd(), filename), 'r') as file: 148 | prev_values = None 149 | is_metadata = False 150 | for line in file.readlines(): 151 | if line.strip() == "": 152 | continue 153 | if is_metadata: 154 | # Parse metadata 155 | values = line.strip().split() 156 | if values[0] == "FLOPS": 157 | summary.flops = float(values[1]) 158 | continue 159 | else: 160 | summary.threads = float(values[1]) 161 | break 162 | if line.strip() == "META_DATA": 163 | # We have reached meta data 164 | is_metadata = True 165 | continue 166 | # This is a normal line of data 167 | values = [float(x) for x in line.strip().split()] 168 | if values[RAW_INDEX.time] == 0: 169 | continue 170 | if not prev_values or prev_values[RAW_INDEX.num_trials] < values[RAW_INDEX.num_trials] or values == []: 171 | prev_values = values 172 | continue 173 | else: 174 | # We want to consider this value 175 | summary.max_gflops = max(summary.max_gflops, prev_values[RAW_INDEX.flops]/prev_values[RAW_INDEX.time]) 176 | summary.x.append(prev_values[RAW_INDEX.working_set_size]) 177 | summary.gflops.append((prev_values[RAW_INDEX.flops]/prev_values[RAW_INDEX.time])/1000) 178 | summary.band.append((prev_values[RAW_INDEX.bytes]/prev_values[RAW_INDEX.time])/1000) 179 | prev_values = values 180 | # Calculate max l1, l2 and dram bandwidth 181 | calculate_bandwidths(summary) 182 | # Calculate weight 183 | calculate_weight(summary) 184 | summaries.append(summary) 185 | print("SUM MAX GFLOPS = {}".format(summary.max_gflops)) 186 | return summaries 187 | 188 | def get_best_summary(summaries): 189 | max_index = 0 190 | max_weight = -math.inf 191 | 192 | max_gflops = -math.inf 193 | 194 | for i, summary in enumerate(summaries): 195 | max_gflops = max(max_gflops, summary.max_gflops) 196 | if summary.weight > max_weight: 197 | max_index = i 198 | max_weight = summary.weight 199 | return summaries[max_index] 200 | 201 | def generate_roofline_plot(summaries): 202 | max_index = 0 203 | max_weight = -math.inf 204 | 205 | max_gflops = -math.inf 206 | 207 | for i, summary in enumerate(summaries): 208 | max_gflops = max(max_gflops, summary.max_gflops) 209 | if summary.weight > max_weight: 210 | max_index = i 211 | max_weight = summary.weight 212 | summary = summaries[max_index] 213 | 214 | x = np.logspace(-1, 5, 1000) 215 | plt.yscale("log") 216 | plt.xscale("log") 217 | 218 | if summary.max_dram: 219 | ys = np.minimum(summary.max_dram * x, np.repeat(summary.max_gflops, len(x))) 220 | # print(ys) 221 | plt.plot(x, ys, label="DRAM {} GB/s".format(summary.max_dram)) 222 | 223 | if summary.max_l1: 224 | ys = np.minimum(summary.max_l1 * x, np.repeat(summary.max_gflops, len(x))) 225 | plt.plot(x, ys, label="L1 {} GB/s".format(summary.max_l1)) 226 | 227 | if summary.max_l2: 228 | ys = np.minimum(summary.max_l2 * x, np.repeat(summary.max_gflops, len(x))) 229 | plt.plot(x, ys, label="L2 {} GB/s".format(summary.max_l2)) 230 | print("MAX GFLOPS = {}".format(summary.max_gflops)) 231 | plt.legend() 232 | plt.xlabel("Flops/byte") 233 | plt.ylabel("MFlops/second") 234 | plt.savefig('roofline.png') 235 | plt.close() 236 | 237 | def generate_bandwidth_plot(summary): 238 | plt.plot(summary.x, summary.band) 239 | plt.xscale("log", basex=2) 240 | plt.yscale("log", basey=10) 241 | plt.ylabel("Total Bandwidth (GB/s)") 242 | plt.xlabel("Working set size (bytes)") 243 | plt.savefig("bandwidth.png") 244 | plt.close() 245 | 246 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/harvard_edge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/drawable-v24/harvard_edge.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 23 | 24 | 28 | 29 | 33 | 34 | 38 | 39 | 40 | 41 | 42 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_cpu_roofline.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 18 | 19 | 32 | 33 | 40 | 41 | 45 | 46 | 47 | 52 | 53 | 57 | 58 | 63 | 64 | 68 | 69 | 74 | 75 | 76 | 84 | 85 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_dsp_roofline.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 24 | 25 | 35 | 36 | 46 | 47 | 57 | 58 | 70 | 71 | 76 | 77 | 91 | 92 | 101 | 102 | 113 | 114 | 126 | 127 | 136 | 137 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_gpu_roofline.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 18 | 19 | 32 | 33 | 40 | 41 | 45 | 46 | 51 | 52 | 56 | 57 | 62 | 63 | 67 | 68 | 73 | 74 | 78 | 79 | 84 | 85 | 93 | 94 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_mixer_roofline.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 | 31 | 32 | 45 | 46 | 47 | 57 | 58 | 68 | 69 | 79 | 80 | 90 | 91 | 101 | 102 | 114 | 115 | 128 | 129 | 142 | 143 | 156 | 157 | 162 | 163 | 171 | 172 | 180 | 181 | 189 | 190 | 203 | 204 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-edge/Gables/f869ec65bc7ea8920af049017355bfc9ed274346/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #A61D31 4 | #70000b 5 | #dd535a 6 | #000000 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 8dp 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 64 4 | 5 | 8 6 | 10 7 | 8 | 8 9 | 12 10 | 11 | 8 12 | 12 13 | 14 | 6 15 | 9 16 | 10 17 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Gables 3 | Settings 4 | Hello World from section: %1$d 5 | CPU Roofline 6 | GPU Roofline 7 | Gables 8 | Generate 9 | 10 | 11 | CPU 12 | CPU+GPU 13 | Others 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 | 23 | 24 | 32 | 33 |