Metrics

GameBench collects performance metrics at configurable intervals. This section details the available metrics and precisely what they mean. It should be noted that our native library is entirely passive by design, i.e. it collects data by observing interactions between your app and the OS, and actions performed by the OS on your app’s behalf. We feel strongly that this ‘zero code’ approach is superior to requiring app developers to explicitly call code in our library or jump through other hoops, especially since many things are hidden .

Frames-per-second (FPS)

The fundamental performance metric. GameBench tracks FPS by monitoring the production and presentation of framebuffers.

CPU utilization % (CPU)

Android: Calculated by reading /proc/self/stat, adding the utime and stime fields together, dividing by the maximum that that number could be (i.e. Linux ‘jiffies’ per second), and finally dividing by the number of CPU cores. This tallies with the same measurement performed by Android Studio.

iOS: Calculated by using the low-level thread_info() API. Unlike Android the result is not divided by the number of cores, i.e. a reading of 200% means 2 cores were working flat-out. This is so it agrees with the same measurement as performed by XCode Instruments.

GPU utilization % (GPU)

Android: As Android offers no on-device APIs to measure GPU utilization, our native library instead talks to GPU drivers directly to access hardware-level performance counters. The readings are used in conjunction with an internal database of maximum GPU clock speeds to determine the percentage of the maximum possible GPU compute. We currently support ARM Mali and Qualcomm Snapdragon GPUs - roughly 90% of the Android GPU market - and hope to add PowerVR in the future.

NB: Android 13+ prevents app-level access to the Qualcomm Adreno driver by default. However, you can still get GPU utilization on these devices if you run this adb command before starting the session: adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/perfcounter"

iOS: Our native library uses the iOS Metal APIs MTLCommandBuffer.gpuStartTime and MTLCommandBuffer.gpuEndTime to determine what percentage of time the GPU was busy. Note that this is fundamentally a slightly different measurement to the percentage of maximum possible compute, which we cannot yet offer on iOS.

Memory usage (MEM)

Android: We use Linux’s Proportional Set Size (PSS), which is defined as the private memory of the process plus the proportion of shared memory with one or more other processes.

iOS: We use the phys_footprint field of struct task_vm_info_data_t obtained with the low-level task_info() API.

Network usage (NET)

Android: We use Android’s TrafficStats APIs to provide the top-level count of bytes transmitted by the application under test. At a lower-level we intercept the standard C library’s ‘socket’ APIs to obtain per-connection byte counts, as well as time-to-first-byte.

iOS: We use multiple methods to passively observe the app’s (indirect) usage of the OS’s lowest-level networking libraries. This means we see as many of the bytes transmitted over the network as possible. Older methods included using NSURLSessionTaskTransactionMetrics and NSURLConnectionDelegate but these APIs omit network activity that don’t go through iOS’s higher-level networking APIs, e.g. libcurl.

Power usage (POW)

Android: We use Android’s BatteryManager API to obtain and report the battery’s current voltage, draw, and overall charge %age, in addition to the battery’s temperature and charging state.

iOS: We use the UIDevice API to obtain the battery charge % and recharging status. Unfortunately the current draw is not available on iOS, but the dashboard will estimate it based on the decrease in charge % over a period of at least 5 minutes (this requires that the device is not charging during the session).

Last updated on