Android NDK之使用 arm-v7a 汇编实现两数之和

关键词: NDK armv7a WebRTC arm汇编 CMake

最近适配对讲程序,在webrtc的库编译的过程中,发现其为arm的平台定制了汇编程序以优化平方根倒数算法速度,上次写汇编还是8086的,借此机会初步尝试下android上arm汇编

具体jni工程建立就不介绍了,Android Studio直接可以从模板创建

工程目录如下

kryo@WSL1:/mnt/k/Android/NDK-Project/XXX/src/main$ tree
.
├── AndroidManifest.xml
├── cpp
│   ├── asm
│   │   ├── CMakeLists.txt
│   │   ├── asm_defines.h
│   │   ├── asm_jni.cpp
│   │   ├── asm_jni.h
│   │   ├── tow_sum_armv7a.S
│   │   └── tow_sum_cpp.cpp
└── java
└── com
└── kryo
├── asm
│   └── TowSumAsm.java
└── ...

1、C++接口编写

asm_jni.h

#ifndef TOW_SUM_AMS_TEST_H
#define TOW_SUM_AMS_TEST_H #include <jni.h> #ifdef USE_ASM
extern "C" int32_t
tow_sum_asm(int32_t *data_in, int32_t *data_out, int32_t data_len, int32_t ret_len, int32_t target);
#else
extern "C" int32_t
tow_sum_cpp(int32_t *data_in, int32_t *data_out, int32_t data_len, int32_t target);
#endif #endif //TOW_SUM_AMS_TEST_H

这里分别使用asm和c代码各自实现一个暴搜版本的两数之和接口。关于asm传递5个参数是有用意的,涉及到函数调用约定,armv7a前4个参数用寄存器传参,超过4个的用栈传递

2、汇编实现

写汇编时我习惯先参考C代码去推导

tow_sum_cpp.cpp

#include "asm_jni.h"

extern "C" int32_t tow_sum_cpp(int32_t *data_in, int32_t *data_out, int32_t data_len,int32_t target) {
for (int i = 0; i < data_len; ++i) {
for (int j = i + 1; j < data_len; ++j) {
if (data_in[i] + data_in[j] == target) {
data_out[0] = i;
data_out[1] = j;
return 0;
}
}
}
data_out[0] = 0;
data_out[1] = 0;
return -1;
}

以下是具体汇编代码的实现,基本每行都给出了注释

tow_sum_armv7a.S

@ Input:(
@ int32_t* data_in, -> r0 &data_in
@ int32_t* data_out,-> r1 &data_out
@ int32_t data_len, -> r2
@ int32_t ret_len, -> r3
@ int32_t target -> [sp])
@ Output: r0 32 bit unsigned integer
@
@ r4: i-index
@ r5: j-index
@ r6: target
@ r7: num1-buff
@ r8: num2-buff
@ r9: sum cache #include "asm_defines.h" GLOBAL_FUNCTION tow_sum
.align 4
DEFINE_FUNCTION tow_sum
push {r4-r11} @ 保存现场 ldr r6, [sp, #32] @ 保存了8个寄存器,偏移8*4bytes取得第5个参数 mov r4, #0 @ 初始化第一个数的索引 i
mov r5, #0 @ 初始化第二个数的索引 j LOOP_1:
sub r9, r2, #1 @ 数组长度-1
cmp r4, r9 @ 判断i是否数组最后一个
beq FAL @ 是就查找失败
mov r5, r4 @ j = i LOOP_2:
add r5, r5, #1 @ j ++
lsl r9, r4, #2 @ 把索引 i 乘4得到地址偏移量
ldr r7, [r0, r9] @ r7 = data_in[i],寄存器相对寻址, r0为 data_in的地址,加上偏移量取的数组元素
lsl r9, r5, #2
ldr r8, [r0, r9] @ 同上得到 r8 = data_in[j]
add r9, r8, r7 @ 两数之和
cmp r9, r6 @ 与目标做比较
beq SUC @ 成功
add r9, r5, #1 @ 没有成功
cmp r9, r2 @ if j < data_len
bne LOOP_2 @ then:下一轮j的查找
add r4, r4, #1 @ else: j没找到,把i++
b LOOP_1 @ 下一轮 i的查找 SUC:
str r4, [r1] @ data_out[0] = i
str r5, [r1, #4] @ data_out[1] = j
mov r0, #0 @ return 0
b END FAL:
mov r4, #0
mov r5, #0
mov r0, #-1 @ return -1
b SUC END:
pop {r4-r11} @ 还原现场
bx lr

3、JNI实现

asm_jni.cpp

#include "asm_jni.h"
#include <android/log.h> #define TAG "ASM_TEST" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) #ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jintArray JNICALL
Java_com_kryo_asm_TowSumAsm_towsum(JNIEnv *env, jobject thiz, jintArray data, jint target) { jintArray r_array = env->NewIntArray(2);
jint *elements_out = env->GetIntArrayElements(r_array, NULL); jsize length = env->GetArrayLength(data);
jint *elements_in = env->GetIntArrayElements(data, NULL); #ifdef USE_ASM
LOGD("call tow_sum_asm !\n");
tow_sum_asm(elements_in, elements_out, (size_t) length, 2, (size_t) target);
#else
LOGD("call tow_sum_cpp !\n");
tow_sum_cpp(elements_in, elements_out, (size_t) length, (size_t) target);
#endif env->ReleaseIntArrayElements(data, elements_in, 0);
env->ReleaseIntArrayElements(r_array, elements_out, 0); return r_array;
}
#ifdef __cplusplus
}
#endif

TowSumAsm.java

public class TowSumAsm {
static {
System.loadLibrary("asm");
}
public native int[] towsum(int[] data, int target);
}

最后贴一下从webrtc开源代码中copy来的asm_defines.h

/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/ #ifndef KRYO_INCLUDE_ASM_DEFINES_H_
#define KRYO_INCLUDE_ASM_DEFINES_H_ #if defined(__linux__) && defined(__ELF__)
.section .note.GNU-stack,"",%progbits
#endif // Define the macros used in ARM assembly code, so that for Mac or iOS builds
// we add leading underscores for the function names.
#ifdef __APPLE__
.macro GLOBAL_FUNCTION name
.global _\name
.private_extern _\name
.endm
.macro DEFINE_FUNCTION name
_\name:
.endm
.macro CALL_FUNCTION name
bl _\name
.endm
.macro GLOBAL_LABEL name
.global _\name
.private_extern _\name
.endm
#else
.macro GLOBAL_FUNCTION name
.global \name
.hidden \name
.endm
.macro DEFINE_FUNCTION name
#if defined(__linux__) && defined(__ELF__)
.type \name,%function
#endif
\name:
.endm
.macro CALL_FUNCTION name
bl \name
.endm
.macro GLOBAL_LABEL name
.global \name
.hidden \name
.endm
#endif // With Apple's clang compiler, for instructions ldrb, strh, etc.,
// the condition code is after the width specifier. Here we define
// only the ones that are actually used in the assembly files.
#if (defined __llvm__) && (defined __APPLE__)
.macro streqh reg1, reg2, num
strheq \reg1, \reg2, \num
.endm
#endif
.text
#endif // KRYO_INCLUDE_ASM_DEFINES_H_

4、CMakeLists.txt编写生成libasm.so

CMakeLists.txt

cmake_minimum_required(VERSION 3.10.2)

project("asm")

ENABLE_LANGUAGE(ASM) #启用汇编支持

if(${ANDROID_ABI} STREQUAL "armeabi-v7a")
add_library(asm SHARED
asm_jni.cpp
tow_sum_armv7a.S)
add_definitions(-DUSE_ASM)
elseif(${ANDROID_ABI} STREQUAL "arm64-v8a")
add_library(asm SHARED
asm_jni.cpp
tow_sum_cpp.cpp)
else()
message(FATAL_ERROR "Unsupported ABI: ${ANDROID_ABI}")
endif() target_link_libraries(asm
log)

5、运行测试

TowSumAsm towSumAsm = new TowSumAsm();
int[] result = towSumAsm.towsum(new int[]{1, 3, 5, 7, 9}, 12);
Log.d(TAG, "result " + result[0] + " " + result[1]);
2024-04-05 10:24:29.269 19863-19863 ASM_TEST                com.kryo.demo                        D  call tow_sum_asm !
2024-04-05 10:24:29.269 19863-19863 JNI_Activity com.kryo.demo D result 1 4

Reference

Android NDK之使用 arm-v7a 汇编实现两数之和的更多相关文章

  1. 对于Android NDK编译器ARM和Thumb模式的理解

    编译NDK项目时,编译器无法识别arm汇编,设置LOCAL_ARM_MODE := arm后问题解决, NDK文档上对LOCAL_ARM_MODE的说明如下: LOCAL_ARM_MODE By de ...

  2. Android NDK开发之Android.mk文件

    Android NDK开发指南---Android.mk文件 博客分类: Android NDK开发指南   Android.mk文件语法详述 介绍: ------------ 这篇文档是用来描述你的 ...

  3. Android NDK开发Crash错误定位[转]

    使用 ndk-stack 的时候需要你的 lib 编译为 debug版的,通常需要下面的修改: 1. 修改 android.mk,增加,为 LOCAL_CFLAGS 增加 -g 选项 2. 修改 ap ...

  4. Android NDK开发入门实例

    AndroidNDK是能使Android应用开发者把从c/c++编译而来的本地代码嵌入到应用包中的一系列工具的组合. 注意: AndroidNDK只能用于Android1.5及以上版本中. I. An ...

  5. [原]如何用Android NDK编译FFmpeg

    我们知道在Ubuntu下直接编译FFmpeg是很简单的,主要是先执行./configure,接着执行make命令来编译,完了紧接着执行make install执行安装.那么如何使用Android的ND ...

  6. 下面就介绍下Android NDK的入门学习过程(转)

    为何要用到NDK? 概括来说主要分为以下几种情况: 1. 代码的保护,由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大. 2. 在NDK中调用第三方C/C++库,因为大部分的开源库 ...

  7. Android NDK 开发(四)java传递数据到C【转】

    转载请注明出处:http://blog.csdn.net/allen315410/article/details/41845701 前面几篇文章介绍了Android NDK开发的简单概念.常见错误及处 ...

  8. Android NDK 开发(二) -- 从Hlello World学起【转】

    转载请注明出处:http://blog.csdn.net/allen315410/article/details/41805719  上篇文章讲述了Android NDK开发的一些基本概念,以及NDK ...

  9. (转)Android: NDK编程入门笔记

    转自: http://www.cnblogs.com/hibraincol/archive/2011/05/30/2063847.html 为何要用到NDK? 概括来说主要分为以下几种情况: 1. 代 ...

  10. Android NDK环境配置

    之前做了一个基于ffmpeg的软解播放器,熟悉了NDK开发的配置环境过程,但是由于太忙一直没有时间写笔记. 首先,介绍一下在这里所参与协作的软件包: 1. JDK: 这个软件被Eclipse依赖. 2 ...

随机推荐

  1. 2020-11-18 原生js实现自动打字效果

    原理 使用定时器,对要输出的文字进行遍历,每遍历一次,都增加一个字以及在段尾加上"|"暗示别人正在打字. js代码 const fangWrite = (theString, qu ...

  2. 程序员应具备的PS基本技能(三):程序员使用PSD源文件切图

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  3. centos8.x阿里源配置

    >>> cd /etc/yum.repo.d >>> mkdir bak >>> mv *.repo bak/ >>> cd b ...

  4. 【系统选型】企业即时通讯(IM)软件调研及供应商对比评估

    企业即时通讯(IM)软件调研及供应商对比评估 1.概览 1.1 即时通讯 即时通讯(Instant messaging,简称IM)是一个终端服务,允许两人或多人使用网路即时的传递文字讯息.档案.语音与 ...

  5. web模块化

    CommonJS-----是一种后端js规范,是nodejs遵循的一种编写js模块的规范引入模块-------require('模块路径')定义模块 ------ exports.模块名= funct ...

  6. 【Azure 微服务】新创建的Service Fabric集群,如何从本地机器上连接到Service Fabric Explorer(Service Fabric状态/错误查看工具)呢?

    问题描述 当在Azure中成功创建一个Service Fabric Cluster 服务后,我们能够在它的Overview页面中发现 Service Fabric Explorer的终结点,但是打开后 ...

  7. 可视化探索开源项目的 contributor 关系

    引语:作为国内外最大的代码托管平台,根据最新的 GitHub 数据,它拥有超 372,000,000 个仓库,其中有 28,000,000 是公开仓.分布式图数据库 NebulaGraph 便是其中之 ...

  8. CXPACKET等待类型分析

    背景 客户反馈今天8点钟开始进入业务高峰期后,数据库的CPU利用率非常高,基本达到了100%,前端应用也非常慢.怀疑是昨晚业务系统升级导致,请我们紧急协助分析. 现象 登录到SQL专家云,进入相关时间 ...

  9. C++ Qt开发:QHostInfo主机地址查询组件

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QHo ...

  10. left jon连接查询踩坑记

    项目开发中经常会使用到多张表进行关联查询,比如left join关联查询. 如果有一张表A和一张表B,查询语句 SELECT a.*,b.name from A a left join B b On ...