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 ...
随机推荐
- 【Azure API 管理】APIM服务资源删除后,为什么不能马上创建相同名称的APIM服务呢?
问题描述 使用Azure APIM服务,在删除旧资源准备新建相同名称的新APIM服务时,尝试多次都是出现"指定的服务名称已正在使用"错误.但实际上同名称的服务已经被删除.为什么多次 ...
- 【Azure Function】Function App和Powershell 集成问题, 如何安装PowerShell的依赖模块
问题描述 在Azure Function中创建一个PowerShell的函数后,其中使用了Get-AzMaintenanceUpdate,New-AzApplyUpdate 等指令,但是在执行时错误. ...
- UG474
为了对工程的资源利用率进行优化,我们首先需要知道当前工程对资源的利用率情况.在Vivado下,我们可以查看工程的资源利用率情况,在下面这张图中,其罗列出了整个工程所使用的资源情况.首先,下面我们需要一 ...
- mysql-批量修改表的主键id,修改成联合主键
1.sql脚本 一. 通过sql脚本,查出所有表的功能,并编写插入修改的联合主键,sql select concat('ALTER table ', TABLE_NAME, ' DROP PRIMAR ...
- 导入Excel文件的时候公式为【#Ref!】应该怎么解决?
前言 在我们使用Excel时,经常会遇到一个问题,就是导入Excel时公式显示为[#Ref!]的情况.这通常是因为公式中引用的单元格已被删除或对应的工作表被删除,导致原公式无法识别对应的参数而显示为[ ...
- 【拉格朗日优化dp】P4365 [九省联考 2018] 秘密袭击 coat
[拉格朗日优化dp]P4365 [九省联考 2018] 秘密袭击 coat 题目简述 求树上所有连通块第 \(k\) 大点权(不足 \(k\) 点记为 \(0\))的和. \(1\leq k\leq ...
- electron-vite 可用,本机软件开发环境搭建
electron-vite 可用,本机软件开发环境搭建 https://electron-vite.github.io/
- nowrap - table td 列 宽度 不被挤 - 大表格制作
nowrap - table td 列 宽度 不被挤 - 大表格制作 表格前几列 设置完宽度,会被右侧动态数据挤没有宽度,加上nowrap,就保证宽度了
- Debian安装KDE的方法
安装KDE指令 sudo apt install kde-plasma-desktop--最小安装:仅安装桌面环境以及基础软件 sudo apt install kde-standard --标准安装 ...
- Spring Boot获取配置参数最简单常用的两种方式
一.自定义属性及常量 在开发过程中,我们常常用到的多环境配置文件,常用的有:dev,test,prod,在不同环境下,我们用到的一样的配置参数,例如:redis,mq,回调接口的url配置.这个情况, ...