Getting Started on Optimizing NDK project for multiple CPU architectures

Sample Project: CPU Features Check(ZIP 1MB)

To squeeze the most performance out of the native part of your application, it could be important to distinguish between different CPU architectures. The separation makes sense for the compiler parameters and for certain parts of the source code, like for optimized assembly instructions.

Using different compiler options depending on the CPU architecture

The Android make system allows you to execute sections of the make files depending on the CPU architecture. This is done with the variable TARGET_ARCH_ABI, which is always set to the CPU architecture you are compiling for.

Example:

ifeq ($(TARGET_ARCH_ABI),x86) 
LOCAL_CFLAGS 	:= -mtune=atom -mssse3
endif
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_CFLAGS	:= -march=armv7-a
Endif

The example illustrates how different optimization parameters can be passed to the compiler. All statements within the ifeq … endif statement will only be evaluated for the specified architecture.

Below is a list of available ABIs.

TARGET_ARCH_ABI
x86
armeabi
armeabi-v7a
mips

Executing CPU architecture dependent code

The Android NDK environment provides three different ways to target code for different CPU architectures: compiler macros, the make system, and the cpufeatures API.

The compiler macros are evaluated at compile time. That means you will not have any runtime overhead or an increase in file size.

The cpufeatures API, on the other hand, will detect at runtime on which CPU architecture your application is running. Beyond the CPU architecture, the API also provides information about which CPU features are supported. For example,  you can query such features as ARM NEON* or Intel SSSE3.

If you are not checking for special CPU features, it is recommended to use compiler macros or the build system to target different CPU architectures.

Using compiler macros

The compiler predefines a macro for the target architecture. The macro can be evaluated with the #ifdef … #endif clause.

#ifdef __i386__
	strlcat(buf, "__i386__", sizeof(buf));
#endif
#ifdef __arm__
	strlcat(buf, "__arm__", sizeof(buf));
#endif
#ifdef _MIPS_ARCH
	strlcat(buf, "_MIPS_ARCH", sizeof(buf));
#endif

Using the make system

  1. Create a source file for every CPU architecture you want to target. You should also create one source file for the default case. Specify the functions that require different implementations for different CPU architectures here.
    Example arch_x86.cpp:
    #include <arch.h>
    
    
    		const char *getTarget () {
    
    		    return "x86";
    
    		} 

    Example arch_default.cpp:
    #include <arch.h>
    
    		const char *getTarget () {
    
    		    return "Default";
    
    		}
  2. Use the function in your program:
    include <arch.h>
    
    		strlcat(buf, getTarget(), sizeof(buf));
     
    The figure below shows the relation between the different files.
  3. Include the version of the arch_xxx.cpp that matches your CPU architecture in your Android.mk file.
    ARCH_SRC_FILES := arch_default.cpp
    
    ifeq ($(TARGET_ARCH_ABI),x86) 
    ARCH_SRC_FILES  := arch_x86.cpp
    endif
    
    ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
    ARCH_SRC_FILES  := arch_arm.cpp
    endif
    
    LOCAL_SRC_FILES += $(ARCH_SRC_FILES)
    Note: In the example we first define a new variable called ARCH_SRC_FILES, which is set to the source file containing the default implementation. This variable will be overwritten for certain CPU architectures. In the last step we assign the content of the ARCH_SRC_FILES variable to the list of local source files (LOCAL_SRC_FILES).

Using the cpufeatures API

  1. Add to your Android.mk file a reference to the cpufeatures library:
    LOCAL_STATIC_LIBRARIES := cpufeatures
  2. Include the cpufeatures header in your C/C++ file:
    #include <cpu-features.h>
  3. Use the function android_getCPU() family to query for the CPU architecture:
    AndroidCpuFamily family = android_getCpuFamily();
    if (family == ANDROID_CPU_FAMILY_X86)
    	strlcat(buf, "ANDROID_CPU_FAMILY_X86", sizeof(buf));
    if (family == ANDROID_CPU_FAMILY_ARM)
    	strlcat(buf, "ANDROID_CPU_FAMILY_ARM", sizeof(buf));
    if (family == ANDROID_CPU_FAMILY_MIPS)
    	strlcat(buf, "ANDROID_CPU_FAMILY_MIPS", sizeof(buf));
    if (family == ANDROID_CPU_FAMILY_UNKNOWN)
    	strlcat(buf, "ANDROID_CPU_FAMILY_UNKNOWN", sizeof(buf));
  4. Use the function android_getCpuFeatures() to query for the supported CPU features:
    AndroidCpuFamily family = android_getCpuFamily();
    uint64_t features = android_getCpuFeatures();
    if (family == ANDROID_CPU_FAMILY_X86) {
    if (features & ANDROID_CPU_X86_FEATURE_SSSE3)
    		strlcat(buf, "nANDROID_CPU_X86_FEATURE_SSSE3", sizeof(buf));
    	if (features & ANDROID_CPU_X86_FEATURE_MOVBE)
    		strlcat(buf, "nANDROID_CPU_X86_FEATURE_MOVBE", sizeof(buf));
    	if (features & ANDROID_CPU_X86_FEATURE_POPCNT)
    		strlcat(buf, "nANDROID_CPU_X86_FEATURE_POPCNT", sizeof(buf));
    
    } else if (family == ANDROID_CPU_FAMILY_ARM) {
    	if (features & ANDROID_CPU_ARM_FEATURE_ARMv7)
    		strlcat(buf, "nANDROID_CPU_ARM_FEATURE_ARMv7", sizeof(buf));
    	if (features & ANDROID_CPU_ARM_FEATURE_VFPv3)
    		strlcat(buf, "nANDROID_CPU_ARM_FEATURE_VFPv3", sizeof(buf));
    	if (features & ANDROID_CPU_ARM_FEATURE_NEON)
    		strlcat(buf, "nANDROID_CPU_ARM_FEATURE_NEON", sizeof(buf));
    	if (features & ANDROID_CPU_ARM_FEATURE_LDREX_STREX)
    		strlcat(buf, "nANDROID_CPU_ARM_FEATURE_LDREX_STREX", sizeof(buf));
    	}
    Note: The result of the android_getCpuFeatures() function is only valid in combination with the CPU family.

Notices

INFORMATION IN THIS DOCUMENT IS PROVIDED IN CONNECTION WITH INTEL PRODUCTS. NO LICENSE, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, TO ANY INTELLECTUAL PROPERTY RIGHTS IS GRANTED BY THIS DOCUMENT. EXCEPT AS PROVIDED IN INTEL'S TERMS AND CONDITIONS OF SALE FOR SUCH PRODUCTS, INTEL ASSUMES NO LIABILITY WHATSOEVER AND INTEL DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY, RELATING TO SALE AND/OR USE OF INTEL PRODUCTS INCLUDING LIABILITY OR WARRANTIES RELATING TO FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR INFRINGEMENT OF ANY PATENT, COPYRIGHT OR OTHER INTELLECTUAL PROPERTY RIGHT.

UNLESS OTHERWISE AGREED IN WRITING BY INTEL, THE INTEL PRODUCTS ARE NOT DESIGNED NOR INTENDED FOR ANY APPLICATION IN WHICH THE FAILURE OF THE INTEL PRODUCT COULD CREATE A SITUATION WHERE PERSONAL INJURY OR DEATH MAY OCCUR.

Intel may make changes to specifications and product descriptions at any time, without notice. Designers must not rely on the absence or characteristics of any features or instructions marked "reserved" or "undefined." Intel reserves these for future definition and shall have no responsibility whatsoever for conflicts or incompatibilities arising from future changes to them. The information here is subject to change without notice. Do not finalize a design with this information.

The products described in this document may contain design defects or errors known as errata which may cause the product to deviate from published specifications. Current characterized errata are available on request.

Contact your local Intel sales office or your distributor to obtain the latest specifications and before placing your product order.

Copies of documents which have an order number and are referenced in this document, or other Intel literature, may be obtained by calling 1-800-548-4725, or go to: http://www.intel.com/design/literature.htm

Software and workloads used in performance tests may have been optimized for performance only on Intel microprocessors. Performance tests, such as SYSmark and MobileMark, are measured using specific computer systems, components, software, operations, and functions. Any change to any of those factors may cause the results to vary. You should consult other information and performance tests to assist you in fully evaluating your contemplated purchases, including the performance of that product when combined with other products.

Any software source code reprinted in this document is furnished under a software license and may only be used or copied in accordance with the terms of that license.

Intel and the Intel logo are trademarks of Intel Corporation in the US and/or other countries.

Copyright © 2012 Intel Corporation. All rights reserved.

*Other names and brands may be claimed as the property of others.