studio r15b r13 para ndk instalar for devices develop android delphi android-ndk arm llvm

r15b - ¿Cómo afectar la generación de código Delphi XEx para objetivos Android/ARM?



instalar android ndk (1)

Estamos investigando el problema. En resumen, depende de la posible desalineación potencial (hasta el límite 32) del Entero al que hace referencia un puntero. Necesito un poco más de tiempo para tener todas las respuestas ... y un plan para abordar esto.

Marco Cantù, moderador en Delphi Developers

Consulte también ¿Por qué las bibliotecas zlib y zip de Delphi son tan lentas por debajo de 64 bits? ya que las bibliotecas Win64 se envían construidas sin optimizaciones.

En el Informe de QP: RSP-9922 Marco agregó la siguiente explicación:

Hay varios problemas aquí:

  • Como se indicó, la configuración de optimización se aplica solo a los archivos de la unidad completa y no a las funciones individuales. En pocas palabras, activar y desactivar la optimización en el mismo archivo no tendrá ningún efecto.
  • Además, simplemente tener habilitada la "Información de depuración" desactiva la optimización. Por lo tanto, cuando uno está depurando, activar explícitamente las optimizaciones no tendrá ningún efecto. En consecuencia, la vista de CPU en el IDE no podrá mostrar una vista desmontada de código optimizado.
  • Tercero, cargar datos de 64 bits no alineados no es seguro y genera errores, de ahí las operaciones separadas de 4 bytes que se necesitan en escenarios dados.

Actualización 2017-05-17. Ya no trabajo para la empresa donde se originó esta pregunta y no tengo acceso a Delphi XEx. Mientras estuve allí, el problema se resolvió migrando a FPC + GCC mixto (Pascal + C), con NEON intrínsecos para algunas rutinas en las que marcó la diferencia. (FPC + GCC es muy recomendable también porque permite el uso de herramientas estándar, particularmente Valgrind). Si alguien puede demostrar, con ejemplos creíbles, cómo puede realmente producir código ARM optimizado de Delphi XEx, me complace aceptar la respuesta .

Los compiladores Delphi de Embarcadero usan un back-end LLVM para producir código ARM nativo para dispositivos Android. Tengo grandes cantidades de código Pascal que necesito compilar en aplicaciones de Android y me gustaría saber cómo hacer que Delphi genere un código más eficiente. En este momento, ni siquiera estoy hablando de características avanzadas como las optimizaciones automáticas SIMD, solo de producir un código razonable. ¿Seguramente debe haber una manera de pasar los parámetros al lado LLVM, o de alguna manera afectar el resultado? Por lo general, cualquier compilador tendrá muchas opciones para afectar la compilación y la optimización del código, pero los objetivos ARM de Delphi parecen ser solo "optimización activada / desactivada" y eso es todo.

Se supone que LLVM es capaz de producir código razonablemente ajustado y sensible, pero parece que Delphi está usando sus instalaciones de una manera extraña. Delphi quiere usar mucho la pila, y generalmente solo utiliza los registros del procesador r0-r3 como variables temporales. Quizás el más loco de todos, parece estar cargando enteros normales de 32 bits como cuatro operaciones de carga de 1 byte. ¿Cómo hacer que Delphi produzca un mejor código ARM, y sin la molestia byte por byte que está haciendo para Android?

Al principio pensé que la carga byte por byte era para intercambiar el orden de bytes de big-endian, pero ese no fue el caso, en realidad solo se trata de cargar un número de 32 bits con 4 cargas de un solo byte. * Podría ser cargar los 32 bits completos sin hacer una carga de memoria de tamaño de palabra no alineada. (si DEBERÍA evitar eso es otra cosa, lo que indicaría que todo es un error del compilador) *

Veamos esta simple función:

function ReadInteger(APInteger : PInteger) : Integer; begin Result := APInteger^; end;

Incluso con las optimizaciones activadas, Delphi XE7 con el paquete de actualización 1, así como XE6, producen el siguiente código de ensamblaje ARM para esa función:

Disassembly of section .text._ZN16Uarmcodetestform11ReadIntegerEPi: 00000000 <_ZN16Uarmcodetestform11ReadIntegerEPi>: 0: b580 push {r7, lr} 2: 466f mov r7, sp 4: b083 sub sp, #12 6: 9002 str r0, [sp, #8] 8: 78c1 ldrb r1, [r0, #3] a: 7882 ldrb r2, [r0, #2] c: ea42 2101 orr.w r1, r2, r1, lsl #8 10: 7842 ldrb r2, [r0, #1] 12: 7803 ldrb r3, [r0, #0] 14: ea43 2202 orr.w r2, r3, r2, lsl #8 18: ea42 4101 orr.w r1, r2, r1, lsl #16 1c: 9101 str r1, [sp, #4] 1e: 9000 str r0, [sp, #0] 20: 4608 mov r0, r1 22: b003 add sp, #12 24: bd80 pop {r7, pc}

Solo cuente la cantidad de instrucciones y accesos a la memoria que Delphi necesita para eso. Y construyendo un entero de 32 bits a partir de 4 cargas de un solo byte ... Si cambio un poco la función y uso un parámetro var en lugar de un puntero, es un poco menos complicado:

Disassembly of section .text._ZN16Uarmcodetestform14ReadIntegerVarERi: 00000000 <_ZN16Uarmcodetestform14ReadIntegerVarERi>: 0: b580 push {r7, lr} 2: 466f mov r7, sp 4: b083 sub sp, #12 6: 9002 str r0, [sp, #8] 8: 6801 ldr r1, [r0, #0] a: 9101 str r1, [sp, #4] c: 9000 str r0, [sp, #0] e: 4608 mov r0, r1 10: b003 add sp, #12 12: bd80 pop {r7, pc}

No incluiré el desmontaje aquí, pero para iOS, Delphi produce un código idéntico para las versiones de puntero y parámetro var, y son casi, pero no exactamente, las mismas que la versión de parámetro var de Android. Editar: para aclarar, la carga byte por byte solo está en Android. Y solo en Android, las versiones de puntero y parámetro var difieren entre sí. En iOS, ambas versiones generan exactamente el mismo código.

A modo de comparación, esto es lo que piensa FPC 2.7.1 (versión troncal SVN de marzo de 2014) de la función con nivel de optimización -O2. Las versiones de puntero y parámetro var son exactamente iguales.

Disassembly of section .text.n_p$armcodetest_$$_readinteger$pinteger$$longint: 00000000 <P$ARMCODETEST_$$_READINTEGER$PINTEGER$$LONGINT>: 0: 6800 ldr r0, [r0, #0] 2: 46f7 mov pc, lr

También probé una función C equivalente con el compilador C que viene con el NDK de Android.

int ReadInteger(int *APInteger) { return *APInteger; }

Y esto se compila esencialmente en lo mismo que hizo FPC:

Disassembly of section .text._Z11ReadIntegerPi: 00000000 <_Z11ReadIntegerPi>: 0: 6800 ldr r0, [r0, #0] 2: 4770 bx lr