Est-il possible de retrouver le temps pris par des millis?

13

La fonction milliss'exécuterait sur une période de 100+ microsecondes ou moins. Existe-t-il un moyen fiable de mesurer le temps pris par un seul appel millis?

Une approche qui vient à l'esprit utilise micros, cependant, un appel à microsinclut également le temps pris par l'appel de fonction microslui-même, donc selon la durée du micros, la mesure millispeut être désactivée.

Je dois trouver cela car une application sur laquelle je travaille nécessite des mesures de temps précises pour chaque étape prise dans le code, y compris millis.

asheeshr
la source
Pouvez-vous clarifier ce que vous demandez ici? Essayez-vous d'obtenir des heures précises à partir de millis () ou essayez-vous de savoir combien de temps il faut pour appeler la fonction millis ()?
Cybergibbons
@Cybergibbons Combien de temps prend un appel miilis.
asheeshr

Réponses:

21

Si vous voulez savoir exactement combien de temps quelque chose prendra, il n'y a qu'une seule solution: regardez le démontage!

En commençant par le code minimal:

void setup(){};

volatile uint16_t x;
void loop()
{
  x = millis();

}

Ce code compilé puis alimenté avr-objdump -Sproduit un démontage documenté. Voici les extraits intéressants:

void loop() produit:

000000a8 <loop>:
  a8:   0e 94 a7 00     call    0x14e   ; 0x14e <millis>
  ac:   60 93 00 01     sts 0x0100, r22
  b0:   70 93 01 01     sts 0x0101, r23
  b4:   80 93 02 01     sts 0x0102, r24
  b8:   90 93 03 01     sts 0x0103, r25
  bc:   08 95           ret

Qui est un appel de fonction ( call), quatre copies (qui copient chacun des octets dans la uint32_tvaleur de retour de millis()(notez que les documents Arduino appellent cela un long, mais ils sont incorrects pour ne pas spécifier explicitement les tailles de variable)), et enfin le retour de fonction.

callnécessite 4 cycles d'horloge, et chacun stsnécessite 2 cycles d'horloge, nous avons donc un minimum de 12 cycles d'horloge juste pour la surcharge d'appel de fonction.

Maintenant, regardons le démontage de la <millis>fonction, qui se trouve à 0x14e:

unsigned long millis()
{
    unsigned long m;
    uint8_t oldSREG = SREG;
 14e:   8f b7           in  r24, 0x3f   ; 63

    // disable interrupts while we read timer0_millis or we might get an
    // inconsistent value (e.g. in the middle of a write to timer0_millis)
    cli();
 150:   f8 94           cli
    m = timer0_millis;
 152:   20 91 08 01     lds r18, 0x0108
 156:   30 91 09 01     lds r19, 0x0109
 15a:   40 91 0a 01     lds r20, 0x010A
 15e:   50 91 0b 01     lds r21, 0x010B
    SREG = oldSREG;
 162:   8f bf           out 0x3f, r24   ; 63

    return m;
}
 164:   b9 01           movw    r22, r18
 166:   ca 01           movw    r24, r20
 168:   08 95           ret

Comme vous pouvez le voir, la millis()fonction est assez simple:

  1. in enregistre les paramètres du registre d'interruption (1 cycle)
  2. cli désactive les interruptions (1 cycle)
  3. lds copier l'un des 4 octets de la valeur actuelle du compteur milli dans un registre temporaire (2 cycles d'horloge)
  4. lds Octet 2 (2 cycles d'horloge)
  5. lds Octet 3 (2 cycles d'horloge)
  6. lds Octet 4 (2 cycles d'horloge)
  7. out restaurer les paramètres d'interruption (1 cycle d'horloge)
  8. movw lecture aléatoire des registres (1 cycle d'horloge)
  9. movw et encore (1 cycle d'horloge)
  10. ret retour de sous-programme (4 cycles)

Donc, si nous les additionnons tous, nous avons un total de 17 cycles d'horloge dans la millis()fonction elle-même, plus une surcharge d'appel de 12, pour un total de 29 cycles d'horloge.

En supposant une fréquence d'horloge de 16 Mhz (la plupart des Arduinos), chaque cycle d'horloge est en 1 / 16e6secondes, ou 0,0000000625 seconde, ce qui correspond à 62,5 nanosecondes. 62,5 ns * 29 = 1,812 microsecondes.

Par conséquent, le temps d'exécution total pour un seul millis()appel sur la plupart des Arduinos sera de 1,812 microsecondes .


Référence d'assemblage AVR

En remarque, il y a de la place pour l'optimisation ici! Si vous mettez à jour la unsigned long millis(){}définition de la fonction inline unsigned long millis(){}, vous supprimez la surcharge d'appel (au prix d' une taille de code légèrement plus grande). De plus, il semble que le compilateur effectue deux mouvements inutiles (les deux movwappels, mais je ne l'ai pas examiné de si près).

Vraiment, étant donné que la surcharge de l'appel de fonction est de 5 instructions et que le contenu réel de la millis()fonction n'est que de 6 instructions, je pense que la millis()fonction devrait vraiment être inlinepar défaut, mais la base de code Arduino est plutôt mal optimisée.


Voici le désassemblage complet pour toute personne intéressée:

sketch_feb13a.cpp.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
    SREG = oldSREG;

    return m;
}

unsigned long micros() {
   0:   0c 94 34 00     jmp 0x68    ; 0x68 <__ctors_end>
   4:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
   8:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
   c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  10:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  14:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  18:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  1c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  20:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  24:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  28:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  2c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  30:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  34:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  38:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  3c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  40:   0c 94 5f 00     jmp 0xbe    ; 0xbe <__vector_16>
  44:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  48:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  4c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  50:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  54:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  58:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  5c:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  60:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>
  64:   0c 94 51 00     jmp 0xa2    ; 0xa2 <__bad_interrupt>

00000068 <__ctors_end>:
  68:   11 24           eor r1, r1
  6a:   1f be           out 0x3f, r1    ; 63
  6c:   cf ef           ldi r28, 0xFF   ; 255
  6e:   d8 e0           ldi r29, 0x08   ; 8
  70:   de bf           out 0x3e, r29   ; 62
  72:   cd bf           out 0x3d, r28   ; 61

00000074 <__do_copy_data>:
  74:   11 e0           ldi r17, 0x01   ; 1
  76:   a0 e0           ldi r26, 0x00   ; 0
  78:   b1 e0           ldi r27, 0x01   ; 1
  7a:   e2 e0           ldi r30, 0x02   ; 2
  7c:   f2 e0           ldi r31, 0x02   ; 2
  7e:   02 c0           rjmp    .+4         ; 0x84 <.do_copy_data_start>

00000080 <.do_copy_data_loop>:
  80:   05 90           lpm r0, Z+
  82:   0d 92           st  X+, r0

00000084 <.do_copy_data_start>:
  84:   a0 30           cpi r26, 0x00   ; 0
  86:   b1 07           cpc r27, r17
  88:   d9 f7           brne    .-10        ; 0x80 <.do_copy_data_loop>

0000008a <__do_clear_bss>:
  8a:   11 e0           ldi r17, 0x01   ; 1
  8c:   a0 e0           ldi r26, 0x00   ; 0
  8e:   b1 e0           ldi r27, 0x01   ; 1
  90:   01 c0           rjmp    .+2         ; 0x94 <.do_clear_bss_start>

00000092 <.do_clear_bss_loop>:
  92:   1d 92           st  X+, r1

00000094 <.do_clear_bss_start>:
  94:   ad 30           cpi r26, 0x0D   ; 13
  96:   b1 07           cpc r27, r17
  98:   e1 f7           brne    .-8         ; 0x92 <.do_clear_bss_loop>
  9a:   0e 94 f0 00     call    0x1e0   ; 0x1e0 <main>
  9e:   0c 94 ff 00     jmp 0x1fe   ; 0x1fe <_exit>

000000a2 <__bad_interrupt>:
  a2:   0c 94 00 00     jmp 0   ; 0x0 <__vectors>

000000a6 <setup>:
  a6:   08 95           ret

000000a8 <loop>:
  a8:   0e 94 a7 00     call    0x14e   ; 0x14e <millis>
  ac:   60 93 00 01     sts 0x0100, r22
  b0:   70 93 01 01     sts 0x0101, r23
  b4:   80 93 02 01     sts 0x0102, r24
  b8:   90 93 03 01     sts 0x0103, r25
  bc:   08 95           ret

000000be <__vector_16>:
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
  be:   1f 92           push    r1
  c0:   0f 92           push    r0
  c2:   0f b6           in  r0, 0x3f    ; 63
  c4:   0f 92           push    r0
  c6:   11 24           eor r1, r1
  c8:   2f 93           push    r18
  ca:   3f 93           push    r19
  cc:   8f 93           push    r24
  ce:   9f 93           push    r25
  d0:   af 93           push    r26
  d2:   bf 93           push    r27
    // copy these to local variables so they can be stored in registers
    // (volatile variables must be read from memory on every access)
    unsigned long m = timer0_millis;
  d4:   80 91 08 01     lds r24, 0x0108
  d8:   90 91 09 01     lds r25, 0x0109
  dc:   a0 91 0a 01     lds r26, 0x010A
  e0:   b0 91 0b 01     lds r27, 0x010B
    unsigned char f = timer0_fract;
  e4:   30 91 0c 01     lds r19, 0x010C

    m += MILLIS_INC;
  e8:   01 96           adiw    r24, 0x01   ; 1
  ea:   a1 1d           adc r26, r1
  ec:   b1 1d           adc r27, r1
    f += FRACT_INC;
  ee:   23 2f           mov r18, r19
  f0:   2d 5f           subi    r18, 0xFD   ; 253
    if (f >= FRACT_MAX) {
  f2:   2d 37           cpi r18, 0x7D   ; 125
  f4:   20 f0           brcs    .+8         ; 0xfe <__vector_16+0x40>
        f -= FRACT_MAX;
  f6:   2d 57           subi    r18, 0x7D   ; 125
        m += 1;
  f8:   01 96           adiw    r24, 0x01   ; 1
  fa:   a1 1d           adc r26, r1
  fc:   b1 1d           adc r27, r1
    }

    timer0_fract = f;
  fe:   20 93 0c 01     sts 0x010C, r18
    timer0_millis = m;
 102:   80 93 08 01     sts 0x0108, r24
 106:   90 93 09 01     sts 0x0109, r25
 10a:   a0 93 0a 01     sts 0x010A, r26
 10e:   b0 93 0b 01     sts 0x010B, r27
    timer0_overflow_count++;
 112:   80 91 04 01     lds r24, 0x0104
 116:   90 91 05 01     lds r25, 0x0105
 11a:   a0 91 06 01     lds r26, 0x0106
 11e:   b0 91 07 01     lds r27, 0x0107
 122:   01 96           adiw    r24, 0x01   ; 1
 124:   a1 1d           adc r26, r1
 126:   b1 1d           adc r27, r1
 128:   80 93 04 01     sts 0x0104, r24
 12c:   90 93 05 01     sts 0x0105, r25
 130:   a0 93 06 01     sts 0x0106, r26
 134:   b0 93 07 01     sts 0x0107, r27
}
 138:   bf 91           pop r27
 13a:   af 91           pop r26
 13c:   9f 91           pop r25
 13e:   8f 91           pop r24
 140:   3f 91           pop r19
 142:   2f 91           pop r18
 144:   0f 90           pop r0
 146:   0f be           out 0x3f, r0    ; 63
 148:   0f 90           pop r0
 14a:   1f 90           pop r1
 14c:   18 95           reti

0000014e <millis>:

unsigned long millis()
{
    unsigned long m;
    uint8_t oldSREG = SREG;
 14e:   8f b7           in  r24, 0x3f   ; 63

    // disable interrupts while we read timer0_millis or we might get an
    // inconsistent value (e.g. in the middle of a write to timer0_millis)
    cli();
 150:   f8 94           cli
    m = timer0_millis;
 152:   20 91 08 01     lds r18, 0x0108
 156:   30 91 09 01     lds r19, 0x0109
 15a:   40 91 0a 01     lds r20, 0x010A
 15e:   50 91 0b 01     lds r21, 0x010B
    SREG = oldSREG;
 162:   8f bf           out 0x3f, r24   ; 63

    return m;
}
 164:   b9 01           movw    r22, r18
 166:   ca 01           movw    r24, r20
 168:   08 95           ret

0000016a <init>:

void init()
{
    // this needs to be called before setup() or some functions won't
    // work there
    sei();
 16a:   78 94           sei

    // on the ATmega168, timer 0 is also used for fast hardware pwm
    // (using phase-correct PWM would mean that timer 0 overflowed half as often
    // resulting in different millis() behavior on the ATmega8 and ATmega168)
#if defined(TCCR0A) && defined(WGM01)
    sbi(TCCR0A, WGM01);
 16c:   84 b5           in  r24, 0x24   ; 36
 16e:   82 60           ori r24, 0x02   ; 2
 170:   84 bd           out 0x24, r24   ; 36
    sbi(TCCR0A, WGM00);
 172:   84 b5           in  r24, 0x24   ; 36
 174:   81 60           ori r24, 0x01   ; 1
 176:   84 bd           out 0x24, r24   ; 36
    // this combination is for the standard atmega8
    sbi(TCCR0, CS01);
    sbi(TCCR0, CS00);
#elif defined(TCCR0B) && defined(CS01) && defined(CS00)
    // this combination is for the standard 168/328/1280/2560
    sbi(TCCR0B, CS01);
 178:   85 b5           in  r24, 0x25   ; 37
 17a:   82 60           ori r24, 0x02   ; 2
 17c:   85 bd           out 0x25, r24   ; 37
    sbi(TCCR0B, CS00);
 17e:   85 b5           in  r24, 0x25   ; 37
 180:   81 60           ori r24, 0x01   ; 1
 182:   85 bd           out 0x25, r24   ; 37

    // enable timer 0 overflow interrupt
#if defined(TIMSK) && defined(TOIE0)
    sbi(TIMSK, TOIE0);
#elif defined(TIMSK0) && defined(TOIE0)
    sbi(TIMSK0, TOIE0);
 184:   ee e6           ldi r30, 0x6E   ; 110
 186:   f0 e0           ldi r31, 0x00   ; 0
 188:   80 81           ld  r24, Z
 18a:   81 60           ori r24, 0x01   ; 1
 18c:   80 83           st  Z, r24
    // this is better for motors as it ensures an even waveform
    // note, however, that fast pwm mode can achieve a frequency of up
    // 8 MHz (with a 16 MHz clock) at 50% duty cycle

#if defined(TCCR1B) && defined(CS11) && defined(CS10)
    TCCR1B = 0;
 18e:   e1 e8           ldi r30, 0x81   ; 129
 190:   f0 e0           ldi r31, 0x00   ; 0
 192:   10 82           st  Z, r1

    // set timer 1 prescale factor to 64
    sbi(TCCR1B, CS11);
 194:   80 81           ld  r24, Z
 196:   82 60           ori r24, 0x02   ; 2
 198:   80 83           st  Z, r24
#if F_CPU >= 8000000L
    sbi(TCCR1B, CS10);
 19a:   80 81           ld  r24, Z
 19c:   81 60           ori r24, 0x01   ; 1
 19e:   80 83           st  Z, r24
    sbi(TCCR1, CS10);
#endif
#endif
    // put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
    sbi(TCCR1A, WGM10);
 1a0:   e0 e8           ldi r30, 0x80   ; 128
 1a2:   f0 e0           ldi r31, 0x00   ; 0
 1a4:   80 81           ld  r24, Z
 1a6:   81 60           ori r24, 0x01   ; 1
 1a8:   80 83           st  Z, r24

    // set timer 2 prescale factor to 64
#if defined(TCCR2) && defined(CS22)
    sbi(TCCR2, CS22);
#elif defined(TCCR2B) && defined(CS22)
    sbi(TCCR2B, CS22);
 1aa:   e1 eb           ldi r30, 0xB1   ; 177
 1ac:   f0 e0           ldi r31, 0x00   ; 0
 1ae:   80 81           ld  r24, Z
 1b0:   84 60           ori r24, 0x04   ; 4
 1b2:   80 83           st  Z, r24

    // configure timer 2 for phase correct pwm (8-bit)
#if defined(TCCR2) && defined(WGM20)
    sbi(TCCR2, WGM20);
#elif defined(TCCR2A) && defined(WGM20)
    sbi(TCCR2A, WGM20);
 1b4:   e0 eb           ldi r30, 0xB0   ; 176
 1b6:   f0 e0           ldi r31, 0x00   ; 0
 1b8:   80 81           ld  r24, Z
 1ba:   81 60           ori r24, 0x01   ; 1
 1bc:   80 83           st  Z, r24
#if defined(ADCSRA)
    // set a2d prescale factor to 128
    // 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
    // XXX: this will not work properly for other clock speeds, and
    // this code should use F_CPU to determine the prescale factor.
    sbi(ADCSRA, ADPS2);
 1be:   ea e7           ldi r30, 0x7A   ; 122
 1c0:   f0 e0           ldi r31, 0x00   ; 0
 1c2:   80 81           ld  r24, Z
 1c4:   84 60           ori r24, 0x04   ; 4
 1c6:   80 83           st  Z, r24
    sbi(ADCSRA, ADPS1);
 1c8:   80 81           ld  r24, Z
 1ca:   82 60           ori r24, 0x02   ; 2
 1cc:   80 83           st  Z, r24
    sbi(ADCSRA, ADPS0);
 1ce:   80 81           ld  r24, Z
 1d0:   81 60           ori r24, 0x01   ; 1
 1d2:   80 83           st  Z, r24

    // enable a2d conversions
    sbi(ADCSRA, ADEN);
 1d4:   80 81           ld  r24, Z
 1d6:   80 68           ori r24, 0x80   ; 128
 1d8:   80 83           st  Z, r24
    // here so they can be used as normal digital i/o; they will be
    // reconnected in Serial.begin()
#if defined(UCSRB)
    UCSRB = 0;
#elif defined(UCSR0B)
    UCSR0B = 0;
 1da:   10 92 c1 00     sts 0x00C1, r1
#endif
}
 1de:   08 95           ret

000001e0 <main>:
#include <Arduino.h>

int main(void)
 1e0:   cf 93           push    r28
 1e2:   df 93           push    r29
{
    init();
 1e4:   0e 94 b5 00     call    0x16a   ; 0x16a <init>

#if defined(USBCON)
    USBDevice.attach();
#endif

    setup();
 1e8:   0e 94 53 00     call    0xa6    ; 0xa6 <setup>

    for (;;) {
        loop();
        if (serialEventRun) serialEventRun();
 1ec:   c0 e0           ldi r28, 0x00   ; 0
 1ee:   d0 e0           ldi r29, 0x00   ; 0
#endif

    setup();

    for (;;) {
        loop();
 1f0:   0e 94 54 00     call    0xa8    ; 0xa8 <loop>
        if (serialEventRun) serialEventRun();
 1f4:   20 97           sbiw    r28, 0x00   ; 0
 1f6:   e1 f3           breq    .-8         ; 0x1f0 <main+0x10>
 1f8:   0e 94 00 00     call    0   ; 0x0 <__vectors>
 1fc:   f9 cf           rjmp    .-14        ; 0x1f0 <main+0x10>

000001fe <_exit>:
 1fe:   f8 94           cli

00000200 <__stop_program>:
 200:   ff cf           rjmp    .-2         ; 0x200 <__stop_program>
Connor Wolf
la source
Wow, bonne réponse! +1
Le gars au chapeau
1) Les quatre stsne doivent pas être comptés comme des frais généraux d'appel: c'est le coût de stockage du résultat dans une variable volatile, ce que vous ne feriez normalement pas. 2) Sur mon système (Arduino 1.0.5, gcc 4.8.2), je n'ai pas le movws. Le coût de l'appel millis()est alors: 4 cycles de surcharge d'appel + 15 cycles en millis()soi = 19 cycles au total (≈ 1,188 µs @ 16 MHz).
Edgar Bonet
1
@EdgarBonet - Cela n'a pas de sens, xest un uint16_t. Cela devrait être de 2 exemplaires au maximum si c'est la cause. Quoi qu'il en soit, la question est de savoir combien de temps millis()prend une fois utilisé , pas lorsqu'il est appelé tout en ignorant le résultat. Étant donné que toute utilisation pratique impliquera de faire quelque chose avec le résultat, j'ai forcé le résultat à être stocké via volatile. Normalement, le même effet serait obtenu par l'utilisation ultérieure de la variable qui est définie sur la valeur de retour de l'appel, mais je ne voulais pas que cet appel supplémentaire prenne de la place dans la réponse.
Connor Wolf
Cela uint16_tdans la source ne correspond pas à l'assembly (4 octets stockés dans la RAM). Vous avez probablement posté la source et le démontage de deux versions différentes.
Edgar Bonet
@ConnorWolf Réponse et explication étonnantes. Je vous remercie!
Lefteris
8

Écrivez une esquisse qui millis mille fois, non pas en faisant une boucle, mais en faisant un copier-coller. Mesurez cela et comparez-le au temps réel prévu. Rappelez-vous que les résultats peuvent varier selon les différentes versions de l'IDE (et son compilateur en particulier).

Une autre option consiste à basculer une broche d'E / S avant et après l'appel millis, puis de mesurer le temps pour une très petite valeur et une valeur un peu plus grande. Comparez les horaires mesurés et calculez les frais généraux.

Le moyen le plus précis est de jeter un œil à la liste de démontage, le code généré. Mais ce n'est pas pour les faibles de cœur. Vous devrez étudier attentivement la fiche technique combien de temps chaque cycle d'instruction prend.

jippie
la source
Comment mesureriez-vous le temps pris par 1 000 millis()appels?
apnorton
Vous savez que millis () est fourni par une interruption sur timer0 qui incrémente une variable interne à chaque tick?
TheDoctor
@TheDoctor avec qui je me suis mélangé delay, vous avez raison. Mais l'idée reste la même, vous pouvez chronométrer un grand nombre d'appels et les calculer en moyenne. Désactiver les interruptions dans le monde n'est peut-être pas une très bonne idée; o)
jippie
Assurez-vous que votre ensemble de données est suffisamment volumineux, car l'impression de caractères sur la série prend quelques millisecondes. Je ne me souviens pas de l'heure exacte, mais je pense que c'est quelque chose comme ~ 0,6 ms par caractère envoyé à Serial.
Steven10172
@ Steven10172 vous pouvez chronométrer une chaîne vide contre une chaîne de 1000 fois (ou plus), puis vous connaissez le delta et la mesure est plus précise.
jippie
3

J'appelle en second lieu des millis à plusieurs reprises, puis je compare le réel au prévu.

Les frais généraux seront minimes, mais leur importance diminuera à mesure que vous appelez millis ().

Si vous regardez

C:\Program Files (x86)\Arduino\Arduino ERW 1.0.5\hardware\arduino\cores\arduino\wiring.c

Vous pouvez voir que millis () est très petit avec seulement 4 instructions (cli is simply # define cli() \__asm__ \__volatile__ ("cli" ::))et un retour.

Je l'appellerais environ 10 millions de fois en utilisant une boucle FOR qui a un volatile comme conditionnel. Le mot-clé volatile empêchera le compilateur de tenter toute optimisation sur la boucle elle-même.

Je ne garantis pas ce qui suit pour être syntaxiquement parfait ..

int temp1,temp2;
temp1=millis();
for (volatile unsigned int j=0;j<1000000;++j){
temp2=millis();}
Serial.print("Execution time = ");
Serial.print((temp2-temp1,DEC);
Serial.print("ms");

ma conjecture est que cela prend ~ 900 ms ou environ 56 us par appel à millis. (Je n'ai pas de guichet automatique Aruduino à portée de main.

80HD
la source
1
Vous devez modifier int temp1,temp2;pour volatile int temp1,temp2;empêcher le compilateur de les optimiser potentiellement.
Connor Wolf
Bon appel aux volatiles. Je voulais vraiment mettre ça et puis non. J'aurais également dû mentionner que la façon de faire un benchmark plus approprié est d'exécuter une boucle vide, d'enregistrer ce temps d'exécution, puis de réexécuter la boucle tout en faisant le travail. Soustrayez la différence, divisez par le nombre d'itérations, et il y a votre temps d'exécution très précis.
80HD
Ce type de référence ne fonctionne que sur un système qui ne préjuge jamais de l' exécution de votre code. L'environnement arduino a par défaut des interruptions périodiques qui s'exécuteront périodiquement. Une meilleure solution serait de basculer une broche à chaque exécution et d'utiliser un minuteur haute résolution pour mesurer le taux de basculement lors de l'exécution et de la non-exécution du code en question, prendre le temps d'exécution minimum sur un certain nombre d'échantillons pour chaque , soustrayez la ligne de base et traitez -la comme votre temps d'exécution. En supposant que votre temps d'exécution est plus court que le temps minimum entre les interruptions.
Connor Wolf