Android NDK: dispositivos ARMv6+VFP. cálculos incorrectos, NaN, números denormales, error VFP11
android-ndk (2)
Esta página tiene una discusión interesante sobre las opciones de ARM FPU: VfpComparison
Creo que si quiere compilar para ARM v6, puede hacer esto: -march=armv6 -mcpu=generic-armv6 -mfloat-abi=softfp
(y omita la opción -mfpu). Si no está apuntando específicamente al procesador que mencionó anteriormente, el genérico armv6 no tiene un fpu garantizado.
Otra opción es probar -mfloat-abi=hard
, en la teoría de que hay un error de compilación en algún lugar alrededor de softfp.
También compruebe si hay corrupción de pila, etc. en su código, es posible que cuando se pasen los valores de coma flotante, los golpee.
PD. También es posible que desee probar un comprobador de punto flotante como TestFloat o la venerable netlib paranoia . Si bien tiene un ejemplo de falla de coma flotante en este procesador en particular y con estas opciones de compilación, no sabe cuán extendido está el problema. Podría ser peor de lo que piensas :)
Deseo dirigir ARMv6 con el dispositivo VFP Android.
Tengo la siguiente línea en mi archivo Android.mk
para habilitar VFP
LOCAL_CFLAGS := -marm -mfloat-abi=softfp -mfpu=vfp -Wmultichar
Creo que apuntar ARMv5
con VFP
.
Edité android-ndk-r8b/toolchains/arm-linux-androideabi-4.6/setup.mk
para eliminar -msoft-float
. También probé con la setup.mk
original.mk
Mi código funciona bien el 99.99% del tiempo, pero algunas veces se vuelve loco en dispositivos ARMv6. Tengo un código especial para detectar cuando se vuelve loco.
Código
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
Calcula la distancia entre 2 puntos. Usualmente es 0.000031 Pero cuando el crazy mode
está activado, es 20.0
El problema no existe cuando lo ejecuto en la CPU ARMv7. Existe solo en la CPU ARMv6.
Creo que debería ser un error común conocido relacionado con la configuración o la versión del compilador. Puede ser que los códigos carezcan de barrera de memoria.
Me gustaría ver alguna referencia a errores similares. Forma de resolverlo O sobre la naturaleza del error.
También a menudo obtengo valores de NaN en ARMv6 cuando el mismo código en ARMv7 no da NaN.
Ya estoy depurando el código durante 2 semanas y buscando en la web. ¡Si alguien pudiera compartir un enlace a un problema similar, sería de gran ayuda!
PD. aquí hay un ejemplo de uno de los comandos de compilación. Intenté muchas configuraciones diferentes ya.
Configuración del compilador
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
ACTUALIZACIÓN 2
Todos estos dispositivos tienen Qualcomm MSM7227A Tiene ARM1136JF-S
Lo que aprendí hasta ahora es que el error podría estar relacionado con las denorms
que leí en alguna parte de las diferencias ARMv7 CON ARMv6 que tiene denorms
ras por defecto y ARM1136SF-S lo tiene opcionalmente. http://infocenter.arm.com/help/topic/com.arm.doc.ddi0211k/DDI0211K_arm1136_r1p5_trm.pdf
Todavía no estoy seguro de cómo verificar esa bandera Flush-To-ZERO en ARM.
ACTUALIZACIÓN 3
El VFP de esta CPU se llama VFP11
. Encontré la --vfp11-denorm-fix
. También hay --vfp-denorm-fix
la erratum en VFP11
cpus. Parece mi problema de destino. Se encontraron pocas publicaciones sobre la errata de VFP11. Espero que arregle el código.
Parece que identifiqué el error.
Es un error en el error de denome de VFP11 (coprocesador ARMv6). los números denormales son muy pequeños.
Obtengo estos números en el código de física implementando la primavera con el dumping
force1 = (Center - P1) * k1 // force1 directed to center
force2 = - Velocity * k2 // force2 directed against velocity
Object->applyForce(force1)
Object->applyForce(force2)
Ambas fuerzas se vuelven muy pequeñas cuando el objeto archieve Center
y yo obtenemos valores denormal
al final.
Puedo volver a escribir Sring y volcar, pero no puedo reescribir el agujero BulletPhysics o todo el código matemático y predecir cada ocurrencia (incluso interna) de un número negativo.
Linker tiene opciones de código de reparación --vfp11-denorm-fix
y --vfp-denorm-fix
http://sourceware.org/binutils/docs-2.19/ld/ARM.html
El enlazador NDK tiene --vfp11-denorm-fix
Esta opción ayuda. El código parece más confiable pero no soluciona el problema al 100%.
Veo menos errores ahora.
BUt si espero sping estabilizar el objeto, entonces finalmente me den denorm -> NaN
Tengo que esperar más, pero llegan los mismos problemas.
Si conoces una solución que corrige el código como --vfp11-denorm-fix
, entonces te doy la recompensa.
Intenté ambos --vfp11-denorm-fix=scalar
y --vfp11-denorm-fix=vector
Flush to Zero bit
int x;
// compiles in ARM mode
asm(
"vmrs %[result],FPSCR /r/n"
"orr %[result],%[result],#16777216 /r/n"
"vmsr FPSCR,%[result]"
:[result] "=r" (x) : :
);
No estoy seguro de por qué, pero requiere LOCAL_ARM_MODE := arm
en Android.mk
Puede ser -mfpu=vfp-d16
lugar de solo vfp
es obligatorio.
Manualmente borre los números denormales
Tengo el código de primavera descrito arriba. Lo mejoré borrando el número denormal manualmente sin usar FPU con la siguiente función.
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;
}
El código original estaba fallando en 15-90 segundos desde el inicio en muchos lugares.
El código actual mostró un problema posiblemente relacionado con este error en uno solo después de 10 minutos de simulación física.
Referencia al error y solución http://sourceware.org/ml/binutils/2006-12/msg00196.html
Dicen que GCC
usa solo código scalr y --vfp11-denorm-fix=scalar
es suficiente. Agrega 1 comando extra para reducir la velocidad. Pero incluso --vfp11-denorm-fix=vector
que agrega 2 comandos extra no es suficiente.
El problema no es más fácil de reproducir. En los teléfonos con una frecuencia más alta de 800Mhz lo veo más a menudo que en uno más lento de 600Mhz. Es posible que se haya solucionado cuando no había CPUs rápidas en el mercado.
Tenemos muchos archivos en proyecto y cada compilación de configuraciones lleva alrededor de 10 minutos. Las pruebas con el estado actual de corrección requieren ~ 10 minutos para jugar en el teléfono. + Calentamos el teléfono debajo de la lámpara. El teléfono caliente muestra errores más rápido.
Deseo probar diferentes configuraciones e informar qué solución es la más eficiente. Pero ahora tenemos que agregar hack para matar el último error posiblemente relacionado con las denomes.
Esperaba encontrar una bala de plata que lo arreglara, pero solo lo -msoft-float
con degradación de rendimiento de 10x o aplicación en ejecución en ARMv7.
Después de reemplazar la función fixDenorm
anterior con la nueva fixDenormE
en Spring / dumping code y aplicar la nueva función para ViewMatrix me libero del último error.
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;
}
}