Android NDK之使用 arm-v7a 汇编实现两数之和
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
- VS Code 上ARM指令集参考文档插件: 32位Code4Leg ,64位ARM A64 Instruction Reference
- Android-Audio-Processing-Using-WebRTC spl_sqrt_floor_arm.S
Android NDK之使用 arm-v7a 汇编实现两数之和的更多相关文章
- 对于Android NDK编译器ARM和Thumb模式的理解
编译NDK项目时,编译器无法识别arm汇编,设置LOCAL_ARM_MODE := arm后问题解决, NDK文档上对LOCAL_ARM_MODE的说明如下: LOCAL_ARM_MODE By de ...
- Android NDK开发之Android.mk文件
Android NDK开发指南---Android.mk文件 博客分类: Android NDK开发指南 Android.mk文件语法详述 介绍: ------------ 这篇文档是用来描述你的 ...
- Android NDK开发Crash错误定位[转]
使用 ndk-stack 的时候需要你的 lib 编译为 debug版的,通常需要下面的修改: 1. 修改 android.mk,增加,为 LOCAL_CFLAGS 增加 -g 选项 2. 修改 ap ...
- Android NDK开发入门实例
AndroidNDK是能使Android应用开发者把从c/c++编译而来的本地代码嵌入到应用包中的一系列工具的组合. 注意: AndroidNDK只能用于Android1.5及以上版本中. I. An ...
- [原]如何用Android NDK编译FFmpeg
我们知道在Ubuntu下直接编译FFmpeg是很简单的,主要是先执行./configure,接着执行make命令来编译,完了紧接着执行make install执行安装.那么如何使用Android的ND ...
- 下面就介绍下Android NDK的入门学习过程(转)
为何要用到NDK? 概括来说主要分为以下几种情况: 1. 代码的保护,由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大. 2. 在NDK中调用第三方C/C++库,因为大部分的开源库 ...
- Android NDK 开发(四)java传递数据到C【转】
转载请注明出处:http://blog.csdn.net/allen315410/article/details/41845701 前面几篇文章介绍了Android NDK开发的简单概念.常见错误及处 ...
- Android NDK 开发(二) -- 从Hlello World学起【转】
转载请注明出处:http://blog.csdn.net/allen315410/article/details/41805719 上篇文章讲述了Android NDK开发的一些基本概念,以及NDK ...
- (转)Android: NDK编程入门笔记
转自: http://www.cnblogs.com/hibraincol/archive/2011/05/30/2063847.html 为何要用到NDK? 概括来说主要分为以下几种情况: 1. 代 ...
- Android NDK环境配置
之前做了一个基于ffmpeg的软解播放器,熟悉了NDK开发的配置环境过程,但是由于太忙一直没有时间写笔记. 首先,介绍一下在这里所参与协作的软件包: 1. JDK: 这个软件被Eclipse依赖. 2 ...
随机推荐
- django学习第十一天---django操作cookie和session
Cookie cookie解析 会话 http协议是无状态的,无连接的 导致每次客户端访问服务端需要登录成功之后才能访问的页面,都需要用户再重新登录一遍,用户体验极差. 客户端想了个办法,cookie ...
- 如何拓展jwt返回的数据
默认的返回值仅有token,我们还需在返回值中增加username和id,方便在客户端页面中显示当前登陆用户 通过修改该视图的返回值可以完成我们的需求. 在user/utils.py中,创建 def ...
- 2-Django之三板斧
HttpResponse 返回字符串类型的数据 HttpResponse: 这是 Django 自带的类,用于构建基本的 HTTP 响应 我的app名称是demo,我们先按照正常的流程,在views中 ...
- STM32标准库内部Flash读写
STM32标准库FLASH读写 1. STM32内部FLASH介绍 STM32系列一般集成有内部flash,这部分内存可以直接通过指针的形式进行读取.但是由于内部flash一般存储为重要数据或程序运行 ...
- STL-RBTree模拟实现
#pragma once #include<assert.h> #include<iostream> using std::cout; using std::endl; usi ...
- C++ STL容器 set类型
C++ STL容器 set类型 set是C++引入的二叉树数据结构 特点: 自动将元素排序 插入和删除查找logn 必须元素支持严格的弱顺序 不能改变元素的值 代码 using Group = std ...
- 2.Canal连接MQ
1. 配置文件介绍 Canal的启动,是以创建实例(instance)的方式,每个实例都有自己单独的工作环境, 而配置也分成两个部分 canal.properties (系统根配置文件) instan ...
- SSH原理与实践(一)
主页 个人微信公众号:密码应用技术实战 个人博客园首页:https://www.cnblogs.com/informatics/ 引言 在日常开发和运维中,我们时常需要通过SSH登录远程主机,进行一些 ...
- STM32标准库通用定时器输入捕获
STM32标准库定时器输入捕获 1.输入捕获介绍 输入捕获为STM32定时器的一个功能,可以用来测量输入信号的频率和占空比. 具体原理:当输入信号经过比较捕获通道时,STM32会依据通道的极性设置决定 ...
- cglib FastClass机制
前言 关于动态代理的一些知识,以及cglib与jdk动态代理的区别,在这一篇已经介绍过,不熟悉的可以先看下. 本篇我们来学习一下cglib的FastClass机制,这是cglib与jdk动态代理的一个 ...