Optimizing the Netflix Android application

27
Optimizing the Netflix Android Application 1 Francois Goldfain Engineering Manager

Transcript of Optimizing the Netflix Android application

Page 1: Optimizing the Netflix Android application

Optimizing the Netflix Android Application

1

Francois Goldfain Engineering Manager

Page 2: Optimizing the Netflix Android application

Images

2

Page 3: Optimizing the Netflix Android application

Minimize Image Resolution! Request different images based on screen resolution, screen density and device memory"! Trade-off between quality and performance"! Storing different image sizes on your server is worth it"! Re-consider your logic and thresholds regularly"

3

86 x 120 6KB to download

20KB in heap (Kindle Fire 2011, RCA Tablet 2014)

150 x 210 10KB to download

61KB in heap (Amazon Fire Tablet 2015)

197 x 276 13KB to download

106KB in heap (Nexus 7 Tablet 2013)

Page 4: Optimizing the Netflix Android application

Leverage WebP format! Compressed image size decreased by at least 30% with no visible quality difference"! Support for alpha & animation makes it a one-stop image format"! Supported natively since Android 4.0"! Software library available for earlier Android versions and for iOS"! Limited browser support"

4

Image Resolution: 592 x 333 File size in JPEG format: 39KB

File size in WebP: 22KB

56% reduction!

Page 5: Optimizing the Netflix Android application

Image Disk Caching! By default, HTTP responses are not cached"

! android.net.HttpResponseCache turns on caching for all http requests (based on Cache-Control)"

! Application-level disk caching allows for greater flexibility"

! Netflix Disk Cache based on Volley"○ Max size varies with device disk space: Between 5MB and 25MB"○ Store cache files under Context.getCacheDir() "○ Least-Recently-Used (LRU) for images, App Session life cycle for other short lived assets"○ Ignore hostname part of URL for images: "http://cdn3.nflximg.net/ipl/13071/d2b1ce2100319d06e5e7dec5423b0e72839b8479.webp

5

Page 6: Optimizing the Netflix Android application

Bitmap Heap Caching! Decoding an image from the disk is CPU intensive"! In-memory L1 Bitmap cache needed for smooth scrolling and efficiency"! BitmapLruCache based on android.util.LruCache"○ HashMap that returns Bitmap object"○ Evict least-recently-used Bitmap when full "

! Decoded Bitmaps in heap much larger than when compressed on disk "! Large Bitmap cache when dealing with lots of images"○ 50% of Runtime.maxMemory() with android:largeHeap="true" ○ Empty cache when application is in background with Application.onTrimMemory() "

6

Page 7: Optimizing the Netflix Android application

Use RGB 565 when possible! By default, images are decoded with Bitmap.Config.ARGB_8888 for optimal quality"○ Alpha channel only pertains to translucent images"○ Additional colors not necessary in many cases"

! Consider using RGB_565 whenever possible"○ Faster decoding"○ Only 2 bytes of heap per pixel, instead of 4"

"

7

BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); decodeOptions.inPreferredConfig = Bitmap.Config.RGB_565; Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); "

RGB 565 337x599

394KB heapARGB 8888

337x599 788KB heap "

Page 8: Optimizing the Netflix Android application

RGB 565 Not Always Right Choice8

Accentuated color banding with RGB 565

Page 9: Optimizing the Netflix Android application

Data Requests

9

Page 10: Optimizing the Netflix Android application

Cost Of Cellular Connection ! Networking biggest battery hog"! Minimize radio time to avoid battery drain"! Understand high cost of sending/receiving data"○ Wake-up and initialize modem"○ Open TCP connection"○ Send/receive data at full cellular power"○ Radio powered for another ~30 seconds after last request is complete"

! Worst scenario when each data request triggers full cycle

10

Page 11: Optimizing the Netflix Android application

Batch Network Requests! Accumulate data requests in a queue and send them all together"! Pre-fetch all data that user might need in the short term"! Piggyback on other network operations to trigger your data requests"

(JobScheduler)

11

Page 12: Optimizing the Netflix Android application

Batch Network Requests (2)12

"https://www.youtube.com/watch?v=7sKC2JBc4dY"

Page 13: Optimizing the Netflix Android application

Batch Network Requests (3)13

Load Home Screen with No Prefetch request

Load Home Screen with Prefetch requests

Prefetch Requests

Subsequent user inputs do not trigger additional data requests

Each subsequent user input do trigger additional data request

Page 14: Optimizing the Netflix Android application

Avoid Polling! Cause of continuous waste of bandwidth and energy"! Unnecessary load on server for nothing"! Leverage Google Cloud Messaging to trigger a refresh

14

Page 15: Optimizing the Netflix Android application

Avoid Polling (2)15

https://www.youtube.com/watch?v=7TXIqa6Ny7Y"

Page 16: Optimizing the Netflix Android application

Prioritize Data Requests! Assign different priorities to data requests stored in the queue"! Consider different queues for data and asset files (images…)"! Cancel/stop data requests at critical times

16

HIGH_PRIORITY

LOW_PRIORITY

Page 17: Optimizing the Netflix Android application

Prioritize Data Requests! Assign different priorities to data requests stored in the queue"! Consider different queues for data and asset files (images…)"! Cancel/stop data requests at critical times

16

https://youtu.be/7IkILwruZLY

Page 18: Optimizing the Netflix Android application

Adopt Efficient Response Format! Prefer binary serialization format (protobuf, flatbuffers…)""

! If using chatty format (XML, JSON), optimize parsing (no reflection).

17

Page 19: Optimizing the Netflix Android application

Use Few Persistent TCP Connections! Cellular network performance degrades with the number of open TCP connections"○ Same bandwidth shared among more TCP connections"○ Limited throughput and increased latency"○ Energy wasted maintaining TCP connections"

18

! Solution: Use few persistent TCP connections"○ Limit number of different server endpoints"○ HTTP Pipelining with HTTP 2"○ Connection Pooling with HTTP 1.1

"! Bonus: Adjust the number of TCP connections with network type

Page 20: Optimizing the Netflix Android application

Terminate TCP Connections! HttpUrlConnection keeps TCP connection open after request (“Keep-Alive”)"! TCP connection closed late causes further battery drain"! Trade off between reusing connections and closing them promptly to reduce energy drain

19

HttpURLConnection closeconn = (HttpURLConnection) url.openConnection(); closeconn.setRequestProperty("connection", "close"); "

Page 21: Optimizing the Netflix Android application

CPU Thrashing

20

Page 22: Optimizing the Netflix Android application

CPU Power States

● Busy loop prevents power management from moving CPU to low power state ● Device power management adjusts chipset power level depending on needs

21

private class VideoThread(){ public void run(){ while(playbackRunnning) { idx = mMediaCodec.dequeueInputBuffer(timeOutUs); //wait 5ms if (idx >= 0) { ...//fill input buffer mMediaCodec.queueInputBuffer(idx,…); } idx = mMediaCodec.dequeueOutputBuffer(..., timeOutUs); //wait 5ms ...//pass output buffer to renderer } } }

Page 23: Optimizing the Netflix Android application

CPU Power States22

private class VideoInputThread(){ public void run(){ while (playbackRunning){ idx = mMediaCodec.dequeueInputBuffer(-1); //wait till buffer is available ... //fill input buffer mMediaCodec.queueInputBuffer(idx,...); } } } private class VideoOutputThread(){ public void run(){ while (playbackRunning){ idx = mMediaCodec.dequeueOutputBuffer(-1); //wait till buffer is available .... //pass output buffer to renderer } } }

● Blocking on decoder allows power management to move CPU to low power state

Page 24: Optimizing the Netflix Android application

Sharing large buffer between C++ & Java! Passing large C++ buffer to Java byte[] involves copying data"! Consider using a direct ByteBuffer instead of byte[] when data is large

23

static jbyteArray VectorToJavaByteArray(JNIEnv* env, std::vector<unsigned char> v) { int32_t size = v.size(); unsigned char * arr = v.data(); // allocate a new Java byte[] and copy the content of c++ buffer jbyteArray jbyteArray = env->NewByteArray(size); env->SetByteArrayRegion(jbyteArray, 0, size, (const jbyte*) arr); return jbyteArray; } "static jobject VectorToJavaByteBuffer(JNIEnv* env, std::vector<unsigned char> v) { int32_t size = v.size(); unsigned char * arr = v.data(); // create a reference to Java ByteBuffer object that holds a pointer to the c++ buffer jobject buffer = env->NewDirectByteBuffer(arr, size); return buffer; }

Page 25: Optimizing the Netflix Android application

Conclusion! Learn about mobile performance patterns""! Profile how your application uses resources"

"! Understand how technical decisions impact performance""! Balance trade-offs

24

Page 26: Optimizing the Netflix Android application

References! Google’s Android Performance Patterns videos"

https://www.youtube.com/playlist?list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE""

! AT&T’s best practices"https://developer.att.com/application-resource-optimizer/docs/best-practices)"

"! Profiling Tools"○ Android Studio’s Network Traffic Tool"○ AT&T ARO (Application Resource Optimizer)"○ Charles Proxy"

25

Page 27: Optimizing the Netflix Android application

Q & A

26