【问题标题】:How to set optimization level for a specific file in Android NDK?如何在 Android NDK 中为特定文件设置优化级别?
【发布时间】:2012-09-21 17:49:43
【问题描述】:

我有一个用于 Android 的本机库,其中包含一些包含 NEON 汇编代码的文件。我从其他一些编码员那里继承了这段代码,并且至少可以说,鉴于我对 NEON 程序集编码(或任何程序集,就此而言)的了解是轻薄的。无论如何,我注意到以下问题:当我使用 'ndk-build NDK_DEBUG=1' 编译时,一切都很好。当我编译发布时,'ndk-build NDK_DEBUG=0',编译器优化掉汇编代码。我已经设法通过破解 ndk 构建脚本并将我的库分成两个来解决这个问题,其中一个库包含所有的程序集文件 - 对于这个库,我以一种非常hacky的方式将优化设置为“-O0” . 所以问题是:如何为特定文件指定优化级别?设置 APP_OPTIM 在 Application.mk 中完成,这会影响所有已编译的文件。 NDK_DEBUG 标志也是如此。

编辑:根据 Alex 的要求,这是我最终使用的 Android.mk,将 lib 分成两部分:一部分带有汇编代码(和 -O0),另一部分带有常规 C 代码(和 -O2):

LOCAL_PATH := $(call my-dir)

# assembly_neon_code_here (neon) module - turn optimization off
include $(CLEAR_VARS)

LOCAL_MODULE := assembly_neon_code_here
LOCAL_SRC_FILES := assembly_neon_code_here.cpp
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
    LOCAL_ARM_NEON := true
endif

LOCAL_CFLAGS := -O0
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog 
include $(BUILD_SHARED_LIBRARY)

# main module
include $(CLEAR_VARS)

LOCAL_MODULE    := complete_lib
LOCAL_SRC_FILES := regular_src1.cpp regular_src2.cpp regular_src3.cpp 
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
    LOCAL_ARM_NEON := true
endif

# allow logcat calls
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
LOCAL_SHARED_LIBRARIES := assembly_neon_code_here
include $(BUILD_SHARED_LIBRARY)

【问题讨论】:

  • 你知道为什么 release build 会删除 neon 汇编代码吗?
  • 这听起来很可疑。优化从未为我删除内联汇编。您想分享您的 Android.mk 还是其中的一部分?
  • 同意可疑部分,同时考虑到我已经验证了通常的优化排除嫌疑人(见下文 - 易失性和内存在 clobber 列表中)。这在某种程度上感觉像是一个错误的汇编代码,但同样,我现在真的不想过多地搞乱它。鬼婆婆 :)

标签: android optimization android-ndk neon


【解决方案1】:

GCC >= 4.4 似乎支持#pragma GCC optimize 来更改中间文件的优化级别,并且还属性optimize 来设置每个函数。

请参阅 http://gcc.gnu.org/onlinedocs/gcc-4.4.7/gcc/Function-Specific-Option-Pragmas.html#Function-Specific-Option-Pragmashttp://gcc.gnu.org/onlinedocs/gcc-4.4.7/gcc/Function-Attributes.html#Function-Attributes

根据这些链接,将#pragma GCC optimize ("O0") 放在导致问题的文件顶部应该可以解决问题。

【讨论】:

  • @fgp 这还能用吗?我无法再让它为我的应用程序工作。
【解决方案2】:

我不知道如何更改每个文件的优化级别,但这样做可能会损害您的应用程序的性能,所以我不建议这样做。我假设汇编代码采用内联汇编块的形式,即汇编代码块与普通 C 或 C++ 代码交错。它看起来如下

asm {
  .. assembly goes here, usually each line in double-quotes, often ending in \n\t
  : ... input operands. Might not be present ...
  : ... output operands. Might not be present ...
  : ... clobber operands. Might not be present ...
}

编译器删除内联汇编块的最可能原因是它不包含输出操作数,或者输出操作数都未使用。您可以通过将内联汇编块标记为 volatile 来避免尝试,这告诉编译器不要弄乱它。为此,只需在 asm 关键字后面写上 volatile

阻止编译器处理内联汇编块的另一件事是将“内存”添加到它们的 clobber 列表中。这告诉编译器程序集正在读取或写入任意内存位置。当您使用它时,还要添加“cc”以获得良好的效果。这告诉编译器内联汇编与条件代码寄存器混淆,即它执行的测试指令会影响后面的条件分支指令的行为。

总之,试着让所有内联汇编块看起来像这样

asm volatile {
  .. whatever ...
  : ... whatever ...
  : ... whatever ...
  : ... whatever ..., "cc", "memory"
}

注意asm 也可以拼写为__asm__volatile 也可以拼写为__volatile__

【讨论】:

  • 嘿 fgp,谢谢您的回复。组装块已经被标记为volatile,并且“memory”是clobber列表的一部分。尽管如此,编译器仍然坚持在设置 -O0 时对其进行优化。我不太担心性能,因为其余代码是用 -O0 编译的,而我使用 -O2 的唯一文件是包含汇编代码的文件。
  • 嗯,这太奇怪了。您使用的是什么版本的 GCC?如果它足够新,您可以尝试使用optimize 属性或#pragma GCC optimize。不过,我不记得哪个 GCC 版本是第一个支持它的。
  • arm-linux-androideabi-g++ (GCC) 4.4.3
  • 添加了第二个答案,描述了#pragma GCC optimize 和属性optimize
猜你喜欢
  • 2016-08-09
  • 1970-01-01
  • 2012-02-02
  • 2012-02-28
  • 1970-01-01
  • 1970-01-01
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多