Using NDK for Performance: Dalvik vs. Native



Updated for non-recursive algorithm, by popular demand

 

NDK is very useful for developing parts of your Android application in native code. The main motivation is performance.

In this example, I wrote a simple SDK+NDK application that uses the same Fibonacci algorithm implemented both in C and Java. The results in performance are astonishing.

The Activity: /src/com/marakana/Fibonacci.java

This is the activity that specifies a simple UI to enter the number for which we want to compute Fibonacci value. It also provides simple output.

 

 

Code:
package com.marakana;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class Fibonacci extends Activity implements OnClickListener {
  TextView textResult;
  Button buttonGo;
  EditText editInput;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // Find UI views
    editInput = (EditText) findViewById(R.id.editInput);
    textResult = (TextView) findViewById(R.id.textResult);
    buttonGo = (Button) findViewById(R.id.buttonGo);
    buttonGo.setOnClickListener(this);
  }

  public void onClick(View view) {
    int input = Integer.parseInt(editInput.getText().toString());
    long start, stop;
    int result;
    String out = "";

    // Dalvik - Recursive
    start = System.currentTimeMillis();
    result = FibLib.fibJ(input);
    stop = System.currentTimeMillis();
    out += String.format("Dalvik recursive: %d (%d msec)", result, stop - start);

    // Dalvik - Iterative
    start = System.currentTimeMillis();
    result = FibLib.fibJI(input);
    stop = System.currentTimeMillis();
    out += String.format("\nDalvik iterative: %d (%d msec)", result, stop - start);

    // Native - Recursive
    start = System.currentTimeMillis();
    result = FibLib.fibN(input);
    stop = System.currentTimeMillis();
    out += String.format("\nNative recursive: %d (%d msec)", result, stop - start);

    // Native - Iterative
    start = System.currentTimeMillis();
    result = FibLib.fibNI(input);
    stop = System.currentTimeMillis();
    out += String.format("\nNative iterative: %d (%d msec)", result, stop - start);

    textResult.setText(out);
  }
}

 


The Layout: res/layout/main.xml

 

 

 

The straight-forward XML layout for the activity above.

 

Code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
  android:orientation="vertical" android:layout_width="fill_parent"
  android:layout_height="fill_parent" android:gravity="center_horizontal">
  <TextView android:layout_width="fill_parent"
    android:layout_height="wrap_content" android:text="@string/app_name"
    android:gravity="center" android:textSize="40sp"
    android:layout_margin="10dp" />
  <LinearLayout android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <EditText android:layout_width="wrap_content"
      android:layout_height="wrap_content" android:id="@+id/editInput"
      android:hint="Input" android:textSize="30sp" android:inputType="number"
      android:layout_margin="10dp"></EditText>
    <Button android:layout_width="wrap_content"
      android:layout_height="wrap_content" android:id="@+id/buttonGo"
      android:text="Go" android:textSize="30sp" android:layout_margin="10sp"></Button>
  </LinearLayout>
  
  <TextView android:layout_width="wrap_content"
    android:layout_height="wrap_content" android:id="@+id/textResult"
    android:text="RESULT" android:textSize="20sp"></TextView>

</LinearLayout>

 

 


JNI Interface to Native Code: /src/com/marakana/FibLib.java

 

 

 

This code represents the JNI interface from Java to the native code. It also provides the pure Java version of Fibonacci function so that we can compare the speed.

 

Code:
package com.marakana;

public class FibLib {

  // Java implementation - recursive
  public static int fibJ(int n) {
    if (n <= 0)
      return 0;
    if (n == 1)
      return 1;
    return fibJ(n - 1) + fibJ(n - 2);
  }

  // Java implementation - iterative
  public static int fibJI(int n) {
    int previous = -1;
    int result = 1;
    for (int i = 0; i <= n; i++) {
      int sum = result + previous;
      previous = result;
      result = sum;
    }
    return result;
  }

  // Native implementation
  static {
    System.loadLibrary("fib");
  }

  // Native implementation - recursive
  public static native int fibN(int n);

  // Native implementation - iterative
  public static native int fibNI(int n);
}

 

 


Auto-generated JNI Header: /jni/com_marakana_FibLib.h

 

Code:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_marakana_FibLib */

#ifndef _Included_com_marakana_FibLib
#define _Included_com_marakana_FibLib
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_marakana_FibLib
 * Method:    fibN
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_com_marakana_FibLib_fibN
  (JNIEnv *, jclass, jint);

/*
 * Class:     com_marakana_FibLib
 * Method:    fibNI
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_com_marakana_FibLib_fibNI
  (JNIEnv *, jclass, jint);

#ifdef __cplusplus
}
#endif
#endif

 

 


C Implementation of the JNI Header: /jni/fib.c

 

 

This is where we provide the actual C version of the Fibonacci function.

Code:
#include "com_marakana_FibLib.h"


jint fibN(jint n) {
  if(n<=0) return 0;
  if(n==1) return 1;
  return fibN(n-1) + fibN(n-2);
}

jint fibNI(jint n) {
  jint previous = -1;
  jint result = 1;
  jint i=0;
  jint sum=0;
  for (i = 0; i <= n; i++) {
    sum = result + previous;
    previous = result;
    result = sum;
  }
  return result;
}

JNIEXPORT jint JNICALL Java_com_marakana_FibLib_fibN
  (JNIEnv *env, jclass obj, jint  n) {
  return fibN(n);
}

JNIEXPORT jint JNICALL Java_com_marakana_FibLib_fibNI
  (JNIEnv *env, jclass obj, jint  n) {
  return fibNI(n);
}

 

 


NDK Makefile: /jni/Android.mk

 

Code:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := fib
LOCAL_SRC_FILES := fib.c

include $(BUILD_SHARED_LIBRARY)

 

 

Create your library using NDK, and run your app. The result should look like this:

On Emulator


On HTC Hero

 

 

 

I think the picture is worth 1000 words. There's clearly a huge (order of magnitude) improvement in running certain computation-intensive code outside Dalvik, when running recursively. Iterative computations are much closer together.

Source:
https://www.protechtraining.com/static/tutorials/Fibonacci.zip
https://www.protechtraining.com/static/tutorials/Fibonacci.apk

 

Published April 8, 2010