android-ndk - Android NDK : ARMv6 + VFP devices. 错误计算、NaN、非正规数、VFP11 bug

标签 android-ndk arm nan armv6

我希望使用 VFP Android 设备来定位 ARMv6。

我的 Android.mk 文件中有以下行来启用 VFP

LOCAL_CFLAGS    := -marm -mfloat-abi=softfp -mfpu=vfp -Wmultichar

我相信我的目标是使用 VFPARMv5

我编辑了 android-ndk-r8b\toolchains\arm-linux-androideabi-4.6\setup.mk 以删除 -msoft-float。我也尝试过原始 setup.mk

我的代码在 99.99% 的情况下都可以正常工作,但有时在 ARMv6 设备上会变得疯狂。 我有特殊的代码来检测它何时变得疯狂。

代码

glm::vec3 D = P1 - P2;
float f1 = sqrtf(D.x*D.x + D.y*D.y + D.z*D.z);
if(!(f1 < 5)){
    // f1 is bigger then 5 or NaN
    mylog_fmt("Crazy %f %f %f %f", P1.x, P1.y, P1.z, f1);
    mylog_fmt("%f %f %f", P2.x, P2.y, P2.z);
}

LogCat:

12-14 00:59:08.214: I/APP(17091): Crazy -20.000031 0.000000 0.000000 20.000000
12-14 00:59:08.214: I/APP(17091): -20.000000 0.000000 0.000000

它计算两点之间的距离。通常为 0.000031 但当疯狂模式开启时,它是20.0

在 ARMv7 CPU 上运行时不存在该问题。它仅存在于 ARMv6 CPU 上。

我相信这应该是一些与编译器设置或版本相关的常见已知错误。可能是代码缺少内存屏障。

我希望看到一些类似错误的引用。有办法解决。或者关于错误的性质。

当 ARMv7 上的相同代码不给出 NaN 时,我也经常在 ARMv6 上得到 NaN 值。

我已经调试代码两周了,并在网上搜索。如果有人可以分享类似问题的链接,那将是一个很大的帮助!

PS。这是编译命令之一的示例。我已经尝试了许多不同的设置。

编译器设置

c:/soft/Android/android-ndk-r8b/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/arm-linux-androideabi-g++
-MMD -MP -MF ./obj/local/armeabi/objs/main/sys/base.o.d -fpic -ffunction-sections -funwind-tables -fstack-protector 
-D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__
-D__ARM_ARCH_5TE__  
-march=armv5te -mtune=arm6 
-mfloat-abi=softfp -mfpu=vfp
-fno-exceptions -fno-rtti -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 
-Ijni/main/ -Ijni/main/sys -Ijni/main/bullet/src -Ijni/main/bullet/src/LinearMath -Ijni/main/bullet/src/BulletCollision/BroadphaseCollision 
-Ijni/main/bullet/src/BulletCollision/CollisionDispatch -Ijni/main/bullet/src/BulletCollision/CollisionShapes -Ijni/main/bullet/src/BulletCollision/NarrowPhaseCollision 
-Ijni/main/bullet/src/BulletDynamics/ConstraintSolver -Ijni/main/bullet/src/BulletDynamics/Dynamics -Ijni/main/../libzip/ -Ic:/soft/Android/android-ndk-r8b/sources/cxx-stl/stlport/stlport 
-Ic:/soft/Android/android-ndk-r8b/sources/cxx-stl//gabi++/include -Ijni/main 
-DANDROID

-marm -march=armv6 -mfloat-abi=softfp -mfpu=vfp -Wmultichar

-Wa,--noexecstack  -frtti  -O2 -DNDEBUG -g   -Ic:/soft/Android/android-ndk-r8b/platforms/android-5/arch-arm/usr/include -c  jni/main/sys/base.cpp
-o ./obj/local/armeabi/objs/main/sys/base.o

更新2

所有这些设备都配备 Qualcomm MSM7227A 它有ARM1136JF-S

到目前为止,我了解到该错误可能与反规范有关 我在某处读到 ARMv7 与 ARMv6 的差异,默认情况下,分母刷新为零,而 ARM1136SF-S 可以选择它。 http://infocenter.arm.com/help/topic/com.arm.doc.ddi0211k/DDI0211K_arm1136_r1p5_trm.pdf

尚不确定如何验证 ARM 上的 Flush-To-ZERO 标志。

更新3

该CPU的VFP称为VFP11 我找到了 --vfp11-denorm-fix 选项。 还有--vfp-denorm-fix 他们纠正了 VFP11 cpu 中的错误。看起来像是我的目标问题。 发现了一些关于 VFP11 勘误表的帖子。希望它能修复代码。

最佳答案

看来我发现了错误。

这是VFP11(ARMv6协处理器)中的错误。 denormal numbers数量非常少。

我在通过转储实现 spring 的物理代码中得到了这个数字

force1 = (Center - P1) * k1         // force1 directed to center 
force2 = - Velocity * k2            // force2 directed against velocity
Object->applyForce(force1)
Object->applyForce(force2)

当对象存档中心时,两种力都变得非常小,并且我在最后得到非正常值。

我可以重写 sring 和转储,但我无法重写 BulletPhysics 或所有数学代码并预测非正规数的每次(甚至内部)出现。

链接器具有修复代码选项--vfp11-denorm-fix--vfp-denorm-fix http://sourceware.org/binutils/docs-2.19/ld/ARM.html

NDK 链接器具有 --vfp11-denorm-fix 此选项有帮助。代码看起来更可靠,但它并不能 100% 解决问题。

我现在看到的错误更少了。

但是如果我等待 sping 稳定对象,那么我最终会得到 denorm -> NaN

我必须等待更长的时间,但同样的问题又出现了。

如果您知道可以修复像 --vfp11-denorm-fix 这样的代码的解决方案,那么我应该给您赏金。

我尝试了 --vfp11-denorm-fix=scalar--vfp11-denorm-fix=vector

刷新至零位

      int x;
      // compiles in ARM mode
      asm(
              "vmrs %[result],FPSCR \r\n"
              "orr %[result],%[result],#16777216 \r\n"
              "vmsr FPSCR,%[result]"
              :[result] "=r" (x) : :
      );

不知道为什么,但它需要 Android.mk 中的 LOCAL_ARM_MODE :=arm 可能需要 -mfpu=vfp-d16 而不仅仅是 vfp

手动清除非正规数

我有上面描述的 Spring 代码。 我通过手动清除非正规数来改进它,而不使用具有以下功能的 FPU。

inline void fixDenorm(float & f){
    union FloatInt32 {
        unsigned int u32;
        float f32;
    };
        FloatInt32 fi;
        fi.f32 = f;

        unsigned int exponent = (fi.u32 >> 23) & ((1 << 8) - 1);
        if(exponent == 0)
            f = 0.f;
}

原始代码在许多地方从开始后 15-90 秒内就失败了。

经过 10 分钟的物理模拟后,当前代码仅在一个地方显示了可能与此错误相关的问题。


错误和修复引用 http://sourceware.org/ml/binutils/2006-12/msg00196.html

他们说 GCC 仅使用标量代码,并且 --vfp11-denorm-fix=scalar 就足够了。 它添加了 1 个额外的命令来减慢速度。但即使添加 2 个额外命令的 --vfp11-denorm-fix=vector 也是不够的。

问题并不容易重现。在频率较高的 800Mhz 手机上,我比在速度较慢的 600Mhz 手机上更常看到这种情况。修复可能是在市场上没有快速 CPU 的情况下完成的。


我们的项目中有很多文件,每个配置编译大约需要 10 分钟。 根据当前修复状态进行测试需要约 10 分钟才能在手机上玩游戏。 + 我们在灯下加热手机。热手机更快地显示错误。

我希望测试不同的配置并报告哪种修复最有效。但现在我们必须添加 hack 来消除最后一个可能与 denorms 相关的错误。

我希望找到能够解决此问题的 Elixir ,但只有性能下降 10 倍的 -msoft-float 或在 ARMv7 上运行应用程序才能做到这一点。


在我在 spring/dumping 代码中用新的 fixDenormE 替换以前的 fixDenorm 函数并为 ViewMatrix 应用新函数后,我摆脱了最后一个错误。

inline void fixDenormE(float & f, float epsilon = 1e-8){
    union Data32 {
        unsigned int u32;
        float f32;
    };
        Data32 d;
        d.f32 = f;

        unsigned int exponent = (d.u32 >> 23) & ((1 << 8) - 1);
        if(exponent == 0)
            f = 0.f;
        if(fabsf(f) < epsilon){
          f = 0.f;
        }
}

关于android-ndk - Android NDK : ARMv6 + VFP devices. 错误计算、NaN、非正规数、VFP11 bug,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13870691/

相关文章:

android - 调用 RILD 层函数在 android 中实现电话 session

android - WebView.draw(canvas) 有时会在某些设备上的 Android Lollipop 5.0+ 中崩溃 - 致命信号 11 (SIGSEGV)

c - 从 C 中的函数返回后数组元素发生变化

linux - 在 ARM 机器上的 Linux 中如何处理系统调用

大负数上的 Javascript parseInt 给出 NaN

javascript - cordova 2.0.0 给出未捕获的类型错误 : Object 0 has no method 'func'

android - 确定使用 Android 应用程序部署的 .so 的工作目录

c++ - 为什么ARM的这条分支指令不起作用

javascript - 在 JavaScript 中如何检查数字是否为 NaN?

python - 使用索引为 pandas DataFrame 中的特定单元格设置值