Guía instructiva para Android*: cómo escribir una aplicación multiproceso con Threading Building Blocks de Intel®

Recientemente publicamos la “Guía instructiva para Windows* 8: cómo escribir una aplicación multiproceso para Windows Store* con Threading Building Blocks de Intel®”. En esa guía dijimos que el motor de cálculo paralelo se puede portar fácilmente a otras plataformas móviles o de escritorio. Android es un buen ejemplo de ese tipo de plataforma móvil.

En una versión estable recientemente publicada de Intel Threading Building Blocks (Intel® TBB), hemos agregado de forma experimental compatibilidad con aplicaciones Android, lo que es decir, bibliotecas de Intel TBB para usar en aplicaciones Android mediante la interfaz JNI. Esta versión se puede descargar desde threadingbuildingblocks.org.

Para iniciar el proceso en un host Linux*, hay que desempaquetar la distribución de código fuente de Intel TBB, obtener el script <unpacked_dir>/build/android_setup.csh y compilar las bibliotecas. Es necesario compilar las bibliotecas porque las versiones para desarrollo solo se distribuyen en forma de código. El archivo <unpacked_dir>/build/index.android.html contiene instrucciones para configurar el entorno y compilar la biblioteca en Linux.

Si suponemos que gnu make 3.81 se encuentra en %PATH% (en una plataforma host Microsoft Windows*) y $PATH (en un host Linux), necesitamos emitir el siguiente comando en el entorno NDK para compilar las bibliotecas Intel TBB para Android:

gmake tbb tbbmalloc target=android

Eso es todo lo que se necesita para compilar la biblioteca; ahora podemos pasar a compilar el ejemplo con Eclipse*. Para el ejemplo de abajo, voy a usar Android SDK Tools Rev.21 y Android NDK Rev 8C en Windows* con el fin de ilustrar el proceso de desarrollo multiplataforma.

Creamos un proyecto con la plantilla predeterminada «New Android Application». Por simplicidad, lo llamamos “app1”, el mismo nombre que en la guía anterior:

Seleccionamos FullscreenActivity como Activity. Eso es todo para la plantilla. Se puede observar que com.example* no es un nombre de paquete aceptable para Google Play*, pero sirve para nuestro ejemplo.

Luego hay que agregar un par de botones al marco principal. Después de agregarlos, el archivo XML del marco principal (app1/res/layout/activity_fullscreen.xml) se verá así:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:background="#0099cc"

    tools:context=".FullscreenActivity" >

    <TextView

        android:id="@+id/fullscreen_content"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:gravity="center"

        android:keepScreenOn="true"

        android:text="@string/dummy_content"

        android:textColor="#33b5e5"

        android:textSize="50sp"

        android:textStyle="bold" />

    <FrameLayout

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:fitsSystemWindows="true" >

        <LinearLayout

            android:id="@+id/fullscreen_content_controls"

            style="?buttonBarStyle"

            android:layout_width="match_parent"

            android:layout_height="74dp"

            android:layout_gravity="bottom|center_horizontal"

            android:background="@color/black_overlay"

            android:orientation="horizontal"

            tools:ignore="UselessParent" >

            <Button

                android:id="@+id/dummy_button1"

                style="?buttonBarButtonStyle"

                android:layout_width="0dp"

                android:layout_height="wrap_content"

                android:layout_weight="1"

                android:text="@string/dummy_button1"

                android:onClick="onClickSR" />

            <Button

                android:id="@+id/dummy_button2"

                style="?buttonBarButtonStyle"

                android:layout_width="0dp"

                android:layout_height="wrap_content"

                android:layout_weight="1"

                android:text="@string/dummy_button2"

                android:onClick="onClickDR" />

        </LinearLayout>

    </FrameLayout>

</FrameLayout>

Y el archivo string (app1/res/values/strings.xml) se verá así

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <string name="app_name">Sample</string>

    <string name="dummy_content">Reduce sample</string>

    <string name="dummy_button1">Simple Reduce</string>

    <string name="dummy_button2">Deterministic Reduce</string>

</resources>

Luego hay que agregar los controladores de botones:

// JNI functions
private native float onClickDRCall();
private native float onClickSRCall();

      public void onClickDR(View myView) {
            TextView tv=(TextView)(this.findViewById(R.id.fullscreen_content));
            float res=onClickDRCall();
            tv.setText("Result DR is n" + res);
      }

      public void onClickSR(View myView) {
            TextView tv=(TextView)(this.findViewById(R.id.fullscreen_content));
            float res=onClickSRCall();
            tv.setText("Result SR is n" + res);
      }

y la biblioteca se carga al archivo FullscreenActivity.java:

@Override

      protected void onCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);

…
            System.loadLibrary("gnustl_shared");

            System.loadLibrary("tbb");

            System.loadLibrary("jni-engine");       

      }

En el caso de la biblioteca "tbb", todo debería estar claro; la biblioteca "gnustl_shared" es necesaria para la compatibilidad con las características de lenguaje C++ de TBB. Sin embargo, para la biblioteca "jni-engine" tenemos que ser más detallados.

"jni-engine" es una biblioteca de ?++ que implementa un motor de cálculo y exporta las interfaces C para llamadas a JNI de nombre onClickSRCall() y onClickSRCall().

De acuerdo con las reglas para desarrollo de NDK, hay que crear una carpeta “jni” dentro del espacio de trabajo y 3 archivos en ella específicos para nuestra biblioteca "jni-engine".

Estos archivos son:

Android.mk (el texto entre signos menor y mayor <> se debe reemplazar con valores reales)

LOCAL_PATH := $(call my-dir)

TBB_PATH := <path_to_the_package>



include $(CLEAR_VARS)

LOCAL_MODULE    := jni-engine

LOCAL_SRC_FILES := jni-engine.cpp

LOCAL_CFLAGS += -DTBB_USE_GCC_BUILTINS -std=c++11 -I$(TBB_PATH)/include

LOCAL_LDLIBS := -ltbb -L./ -L$(TBB_PATH)/<path_to_libtbb_so>

include $(BUILD_SHARED_LIBRARY)



include $(CLEAR_VARS)

LOCAL_MODULE    := libtbb

LOCAL_SRC_FILES := libtbb.so

include $(PREBUILT_SHARED_LIBRARY)

Application.mk

APP_ABI := x86

APP_GNUSTL_FORCE_CPP_FEATURES := exceptions rtti

APP_STL := gnustl_shared

jni-engine.cpp:

#include <jni.h>



#include "tbb/parallel_reduce.h"

#include "tbb/blocked_range.h"

float SR_Click()

{

    int N=10000000;

    float fr = 1.0f/(float)N;

    float sum = tbb::parallel_reduce(

        tbb::blocked_range<int>(0,N), 0.0f,

        [=](const tbb::blocked_range<int>& r, float sum)->float

        {

            for( int i=r.begin(); i!=r.end(); ++i )

                sum += fr;

            return sum;

        },

        []( float x, float y )->float

        {

            return x+y;

        }

    );

    return sum;  

}



float DR_Click()

{

    int N=10000000;

    float fr = 1.0f/(float)N;

    float sum = tbb::parallel_deterministic_reduce(

        tbb::blocked_range<int>(0,N), 0.0f,

        [=](const tbb::blocked_range<int>& r, float sum)->float

        {

            for( int i=r.begin(); i!=r.end(); ++i )

                sum += fr;

            return sum;

        },

        []( float x, float y )->float

        {

            return x+y;

        }

    );     

    return sum;  

}



 extern "C" JNIEXPORT jfloat JNICALL Java_com_example_app1_FullscreenActivity_onClickDRCall(JNIEnv *env, jobject obj)

{

    return DR_Click();

}



extern "C" JNIEXPORT jfloat JNICALL Java_com_example_app1_FullscreenActivity_onClickSRCall(JNIEnv *env, jobject obj)

{

    return SR_Click();

}

Usamos los mismos algoritmos que en la guía anterior.

Cuando usamos el NDK para compilar, compila las bibliotecas a las carpetas correspondientes, incluidas nuestras bibliotecas libjni-engine.so, libgnustl_shared.so y libtbb.so.

A continuación, hay que volver a Eclipse y compilar el archivo app1.apk. Ahora la aplicación está lista para instalarse en el AVD o en hardware real. En el AVD se ve así:

 

¡Y terminamos! Esta aplicación sencilla está lista y debería ser un buen primer paso hacia la escritura de una aplicación paralela más compleja para Android. Y para aquellos que usaron código de la guía anterior, la aplicación se pudo portar con éxito a Android.

* Es posible que la propiedad de otros nombres y marcas corresponda a terceros.

Related Articles and Resources: