La función millis
se estaría ejecutando en el lapso de más de 100 microsegundos o menos. ¿Hay alguna manera confiable de medir el tiempo que toma una sola llamada millis?
Un enfoque que viene a la mente es usar micros
, sin embargo, un llamado a micros
incluir también el tiempo que toma la llamada a la función de micros
sí mismo, por lo que, dependiendo de cuánto tiempo tome el micros, la medición millis
puede estar desactivada.
Necesito encontrar esto porque una aplicación en la que estoy trabajando requiere mediciones precisas de tiempo para cada paso dado en el código, incluido millis
.
miilis
lleva una llamada .Respuestas:
Si desea saber exactamente cuánto tiempo tomará algo, solo hay una solución: ¡mire el desmontaje!
Comenzando con el código mínimo:
Este código compilado y luego alimentado
avr-objdump -S
produce un desmontaje documentado. Aquí están los extractos interesantes:void loop()
produce:Que es una llamada a función (
call
), cuatro copias (que copian cada uno de los bytes en eluint32_t
valor de retorno demillis()
(tenga en cuenta que los documentos arduino llaman a esto along
, pero son incorrectos para no especificar explícitamente los tamaños de las variables)), y finalmente el función de retorno.call
requiere 4 ciclos de reloj, y cada unosts
requiere 2 ciclos de reloj, por lo que tenemos un mínimo de 12 ciclos de reloj solo para la sobrecarga de llamadas de función.Ahora, veamos el desmontaje de la
<millis>
función, que se encuentra en0x14e
:Como puede ver, la
millis()
función es bastante simple:in
guarda la configuración del registro de interrupción (1 ciclo)cli
apaga las interrupciones (1 ciclo)lds
Copie uno de los 4 bytes del valor actual del contador mili en un registro temporal (2 ciclos de reloj)lds
Byte 2 (2 ciclos de reloj)lds
Byte 3 (2 ciclos de reloj)lds
Byte 4 (2 ciclos de reloj)out
restaurar la configuración de interrupción (1 ciclo de reloj)movw
shuffle registra alrededor (1 ciclo de reloj)movw
y nuevamente (1 ciclo de reloj)ret
regreso de la subrutina (4 ciclos)Entonces, si los sumamos todos, tenemos un total de 17 ciclos de reloj en la
millis()
función en sí, más una sobrecarga de llamadas de 12, para un total de 29 ciclos de reloj.Suponiendo una velocidad de reloj de 16 Mhz (la mayoría de los arduinos), cada ciclo de reloj es
1 / 16e6
segundos, o 0.0000000625 segundos, que es 62.5 nanosegundos. 62.5 ns * 29 = 1.812 microsegundos.Por lo tanto, el tiempo de ejecución total para una sola
millis()
llamada en la mayoría de los Arduinos será de 1.812 microsegundos .Referencia de ensamblaje AVR
Como nota al margen, ¡hay espacio para la optimización aquí! Si actualiza la
unsigned long millis(){}
definición de la función para que seainline unsigned long millis(){}
, eliminaría la sobrecarga de la llamada (a costa de un tamaño de código ligeramente mayor). Además, parece que el compilador está haciendo dos movimientos innecesarios (las dosmovw
llamadas, pero no lo he mirado tan de cerca).Realmente, considerando que la sobrecarga de la llamada a la función son 5 instrucciones, y el contenido real de la
millis()
función es solo 6 instrucciones, creo que lamillis()
función realmente debería serinline
por defecto, pero la base de código Arduino está bastante mal optimizada.Aquí está el desensamblaje completo para cualquier persona interesada:
fuente
sts
no deben contarse como gastos generales de llamadas: este es el costo de almacenar el resultado en una variable volátil, que normalmente no haría. 2) En mi sistema (Arduino 1.0.5, gcc 4.8.2), no tengo elmovw
s. Entonces el costo de la llamadamillis()
es: 4 ciclos de sobrecarga de la llamada + 15 ciclos enmillis()
sí mismo = 19 ciclos en total (≈ 1.188 µs @ 16 MHz).x
es unuint16_t
. Debe ser de 2 copias como máximo si esa es la causa. De todos modos, la pregunta es cuánto tiempomillis()
toma cuando se usa , no cuando se llama mientras se ignora el resultado. Como cualquier uso práctico implicará hacer algo con el resultado, forcé el resultado a almacenarse mediantevolatile
. Normalmente, se lograría el mismo efecto con el uso posterior de la variable que se establece en el valor de retorno de la llamada, pero no quería que esa llamada adicional ocupara espacio en la respuesta.uint16_t
en la fuente no coincide con el ensamblado (4 bytes almacenados en la RAM). Probablemente haya publicado la fuente y el desmontaje de dos versiones diferentes.Escriba un boceto que mil veces 1000, no haciendo un bucle, sino copiando y pegando. Mida eso y compárelo con el tiempo real esperado. Tenga en cuenta que los resultados pueden variar con diferentes versiones del IDE (y su compilador en particular).
Otra opción es alternar un pin IO antes y después de la llamada millis, luego medir el tiempo para un valor muy pequeño y un valor algo mayor. Compare los tiempos medidos y calcule los gastos generales.
La forma más precisa es echar un vistazo a la lista de desmontaje, el código generado. Pero eso no es para los débiles de corazón. Tendrá que estudiar cuidadosamente la hoja de datos cuánto dura cada ciclo de instrucción.
fuente
millis()
llamadas?delay
, tienes razón. Pero la idea sigue siendo la misma, puede programar una gran cantidad de llamadas y promediarlas. Sin embargo, apagar las interrupciones a nivel mundial puede no ser una muy buena idea; o)En segundo lugar, llamo a Millis repetidamente y luego comparo lo real con lo esperado.
Habrá algunos gastos generales mínimos, pero disminuirá en importancia cuanto más veces llame a millis ().
Si miras
Puede ver que millis () es muy pequeño con solo 4 instrucciones
(cli is simply # define cli() \__asm__ \__volatile__ ("cli" ::))
y un retorno.Lo llamaría aproximadamente 10 millones de veces usando un bucle FOR que tiene un volátil como condicional. La palabra clave volátil evitará que el compilador intente cualquier optimización en el propio ciclo.
No garantizo que lo siguiente sea sintácticamente perfecto ...
Supongo que eso toma ~ 900ms o aproximadamente 56us por llamada a millis. (No tengo un cajero automático a mano aruduino.
fuente
int temp1,temp2;
avolatile int temp1,temp2;
para evitar que el compilador los optimice potencialmente.