Quantcast
Channel: Moving Forward » Android
Viewing all articles
Browse latest Browse all 7

Reasoning About Phone TCP Performance

$
0
0

Here’s a question that has often been asked, but seldom been answered: how does one send an image to or from a phone as efficiently and quickly as possible? More generally, how can we reason about the behavior of TCP/IP as implemented on a modern smart phone? Phones don’t seem to play by the rules which TCP was designed for, we have a terribly unreliable channel pushing packets at random, with terrible delivery rates. This is exactly the kind of scenario that would wreak havoc with congestion avoidance and control algorithms, when in reality there might not be any valid reason to artificially throttle the connection. Not much work into how TCP performs in the mobile world has gone into commercial devices and I think it’s a really big open question that deserves some attention.

To that end I’ve started looking at characterizing TCP delivery on my mobile phone, in the hopes that from this characterization I can develop some more efficient algorithms, and just get an intuitive sense for what can and cannot be offloaded to the server. A big idea that has started gaining some traction lately is heterogeneous computing, where computationally expensive tasks are offloaded from your phone to the cloud for processing. Knowing the abilities of the network data channel will play a big roll in determining the feasibility of this, and many other ideas.

Let’s Transfer Some Images

My current side project is centered around manipulating images on the mobile phone, or in the cloud, so a good starting point in this study was to see how long it takes to upload and download images of various sizes, over HTTP. I used three image sizes:

  • Small – 11KB
  • Medium – 51KB
  • Large – 86KB

I took these sizes from the three thumbnail sizes Facebook’s Haystack image store uses to present images to the end user, as those sizes seem to be working reasonably well for them. A large image is by no means a full-sized image, but it’s more than big enough to display on any smartphone screen, with some zooming supported.

Because this is a first pass, the implementation is pretty rough, and leaves room for optimization. My goal was not to see what the most optimal way to send an image across the wire was, but it was to characterize the most obvious way a typical developer might do it. I used the pre-bundled HTTP classes, and did everything the obvious way. I wrote two functions and wrapped them up in an Android service, with some logging facilities.

The functions themselves looked like this:

Downloading

        private int doDownload(String path) {
            long startTime = System.currentTimeMillis();

            try {
                URL url = new URL(path);
                URLConnection connection = url.openConnection();

                // getting file length, will cause the first
                // read of the HTTP headers
                int lenghtOfFile = connection.getContentLength();

                // create an input stream to grab data from the connection
                InputStream input = new BufferedInputStream(connection.getInputStream(), 8192);

                // do a read to null basically and grab the entire image, forcing
                // the internals of the stream implementations to do what they must
                // do to push the image to us.
                byte data[] = new byte[1024];
                long total = 0;
                int count;
                while ((count = input.read(data)) != -1) {
                    total += count;
                }

                // close the connection
                input.close();
            } catch (Exception e) {
                return -1;
            }

            long stopTime = System.currentTimeMillis();

            return (int) (stopTime - startTime);
        }

Uploading

        private int doUpload(String filePath, String path) {
            long startTime = System.currentTimeMillis();

            try {
                // No simple way to do a multipart post, so we
                // build it from scratch in a sense.
                HttpClient httpClient = new DefaultHttpClient();
                HttpContext localContext = new BasicHttpContext();
                HttpPost httpPost = new HttpPost(path);

                MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
                entity.addPart("image", new FileBody(new File (filePath)));

                httpPost.setEntity(entity);
                HttpResponse response = httpClient.execute(httpPost, localContext);
            } catch (IOException e) {
                e.printStackTrace();
                return -1;
            }

            long stopTime = System.currentTimeMillis();

            return (int) (stopTime - startTime);
        }

Quite frankly, they leave a lot of questions to be answered. To really understand what’s going on, we need to dive into the internals of the BufferedInputString class, and do some TCP dump captures of the packet data. It’s of great interest to see how the request headers and body are broken up, controlling TCP packet segmentation could have big gains when transferring images. As I said before however, this is only preliminary exploration, so we’ll leave all that to be answered in the future.

Results

I left my sample application service running on my phone as I went for a run over the course of two hours in the city. I figured this would give me a decent spread of typical mobile conditions, in a diverse set of areas, and give decent data. I collected approximately 1500 data points and produced some histograms of the data.

Taking a look at the data already yields some really interesting insights. Images are generally transfered within 2-3 seconds, that’s the common case, with an extremely long tail of extended delivery. Interestingly enough I didn’t have a single timeout, but some images took over a minute to transfer, which is completely unacceptable for the end-user experience, considering the size of the images involved.

The difference in size as compared to transfer time was approximately linear. Uploading of the small image seemed to exceed expectations, and didn’t actually have much of a long tail like other transfers. I’m guessing the length of the tail is a function of the number of round-trips required to successfully transfer the data. Examining the packet flow of these images would yield some interesting insight into that relationship.

Conclusions

So this really just creates a bunch of further questions. It’s a really high-level view of delivery times for small files on the phone, but closer examination could yield some more interesting information. I’m really curious about applications of this knowledge in designing efficient image uploading algorithms. Relaxing some of the guarantees of TCP might allow for significantly faster image upload, and data transfers more suited for mobile apps. Expect to see more along this vector in the blog in the future!


Viewing all articles
Browse latest Browse all 7

Trending Articles