Có thể tìm thấy thời gian của millis?


13

Chức năng millisnày sẽ chạy trong khoảng từ hơn 100 micro giây trở xuống. Có cách nào đáng tin cậy để đo thời gian thực hiện trong một cuộc gọi millis không?

microsTuy nhiên, một cách tiếp cận mà bạn đang sử dụng là một cuộc gọi microssẽ bao gồm cả thời gian thực hiện cuộc gọi chức năng của microschính nó, do đó tùy thuộc vào thời gian micros mất bao lâu, phép đo milliscó thể bị tắt.

Tôi cần tìm thấy đây là một ứng dụng tôi đang làm việc yêu cầu các phép đo thời gian chính xác cho mỗi bước được thực hiện trong mã, bao gồm cả millis.


Bạn có thể làm rõ những gì bạn đang hỏi ở đây? Bạn đang cố gắng để có được thời gian chính xác từ millis () hoặc bạn đang cố gắng tính xem mất bao lâu để gọi hàm millis ()?
Cyberg Ribbon

@Cyberg Ribbon Cuộc gọi sẽ miilismất bao lâu .
asheeshr

Câu trả lời:


21

Nếu bạn muốn biết chính xác một cái gì đó sẽ mất bao lâu, chỉ có một giải pháp: Hãy nhìn vào sự tháo gỡ!

Bắt đầu với mã tối thiểu:

void setup(){};

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

}

Mã này được biên dịch và sau đó đưa vào avr-objdump -Ssản xuất một tài liệu tháo gỡ. Dưới đây là những trích đoạn thú vị:

void loop() sản xuất:

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

Đó là một hàm gọi ( call), bốn bản sao (sao chép từng byte trong uint32_tgiá trị trả về của millis()(lưu ý rằng các tài liệu arduino gọi đây là a long, nhưng chúng không chính xác để không chỉ định rõ ràng các kích thước biến)) và cuối cùng là Hàm trả về.

callyêu cầu 4 chu kỳ đồng hồ và mỗi stsyêu cầu 2 chu kỳ đồng hồ, vì vậy chúng tôi có tối thiểu 12 chu kỳ đồng hồ chỉ dành cho chức năng gọi qua chức năng.

Bây giờ, hãy xem xét sự tháo gỡ của <millis>chức năng, được đặt tại 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

Như bạn có thể thấy, millis()chức năng này khá đơn giản:

  1. in lưu cài đặt thanh ghi ngắt (1 chu kỳ)
  2. cli tắt ngắt (1 chu kỳ)
  3. lds sao chép một trong 4 byte giá trị hiện tại của bộ đếm milli vào một thanh ghi tạm thời (2 chu kỳ đồng hồ)
  4. lds Byte 2 (2 chu kỳ đồng hồ)
  5. lds Byte 3 (2 chu kỳ đồng hồ)
  6. lds Byte 4 (2 chu kỳ đồng hồ)
  7. out khôi phục cài đặt ngắt (1 chu kỳ đồng hồ)
  8. movw đăng ký xáo trộn xung quanh (1 chu kỳ đồng hồ)
  9. movw và một lần nữa (1 chu kỳ đồng hồ)
  10. ret trở về từ chương trình con (4 chu kỳ)

Vì vậy, nếu chúng ta thêm tất cả chúng lên, chúng ta có tổng cộng 17 chu kỳ đồng hồ trong millis()chức năng, cộng với tổng số cuộc gọi là 12, cho tổng số 29 chu kỳ đồng hồ.

Giả sử tốc độ xung nhịp 16 Mhz (hầu hết các arduinos), mỗi chu kỳ xung nhịp là 1 / 16e6giây, hoặc 0,0000000625 giây, tức là 62,5 nano giây. 62,5 ns * 29 = 1,812 micro giây.

Do đó, tổng thời gian thực hiện cho một millis()cuộc gọi trên hầu hết các Arduinos sẽ là 1.812 micro giây .


Tài liệu tham khảo hội

Như một lưu ý phụ, có không gian để tối ưu hóa ở đây! Nếu bạn cập nhật unsigned long millis(){}định nghĩa hàm thành inline unsigned long millis(){}, bạn sẽ loại bỏ chi phí cuộc gọi (với chi phí kích thước mã lớn hơn một chút ). Hơn nữa, có vẻ như trình biên dịch đang thực hiện hai động tác không cần thiết (hai movwcuộc gọi, nhưng tôi đã không xem xét kỹ về nó).

Thực sự, xem xét tổng phí gọi chức năng là 5 hướng dẫn và nội dung thực tế của millis()chức năng chỉ có 6 hướng dẫn, tôi nghĩ rằng millis()chức năng này thực sự nên được inlinemặc định, nhưng cơ sở mã Arduino được tối ưu hóa khá kém.


Dưới đây là toàn bộ cho bất cứ ai quan tâm:

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>

Wow, câu trả lời tuyệt vời! +1
Chàng trai với chiếc mũ

1) Bốn stskhông nên được tính là chi phí cuộc gọi: đây là chi phí lưu trữ kết quả trong một biến dễ bay hơi, điều mà bạn thường không làm. 2) Trên hệ thống của tôi (Arduino 1.0.5, gcc 4.8.2), tôi không có movws. Sau đó, chi phí của cuộc gọi millis()là: 4 chu kỳ của tổng phí cuộc gọi + 15 chu kỳ trong millis()chính nó = tổng số 19 chu kỳ (≈ 1.188 Gõ @ 16 MHz).
Edgar Bonet

1
@EdgarBonet - Điều đó không có ý nghĩa, xlà một uint16_t. Nên có tối đa 2 bản nếu đó là nguyên nhân. Dù sao, câu hỏi là bao lâu millis()mất khi sử dụng , không phải khi gọi trong khi bỏ qua kết quả. Vì bất kỳ việc sử dụng thực tế nào cũng sẽ liên quan đến việc làm một cái gì đó với kết quả, tôi buộc kết quả phải được lưu trữ thông qua volatile. Thông thường, hiệu ứng tương tự sẽ đạt được bằng cách sử dụng biến sau đó được đặt thành giá trị trả về của cuộc gọi, nhưng tôi không muốn có thêm cuộc gọi đó chiếm không gian trong câu trả lời.
Sói Connor

Điều này uint16_ttrong nguồn không khớp với cụm (4 byte được lưu trữ vào RAM). Bạn có thể đăng nguồn và tháo gỡ của hai phiên bản khác nhau.
Edgar Bonet

@ConnorWolf Câu trả lời và giải thích tuyệt vời. Cảm ơn bạn!
Lefteris

8

Viết một bản phác thảo hàng nghìn lần, không phải bằng cách tạo một vòng lặp, mà bằng cách sao chép và dán. Đo lường và so sánh nó với thời gian dự kiến ​​thực tế. Xin lưu ý rằng các kết quả có thể thay đổi theo các phiên bản khác nhau của IDE (và cụ thể là trình biên dịch của nó).

Một tùy chọn khác là chuyển đổi chân IO trước và sau cuộc gọi millis, sau đó đo thời gian cho một giá trị rất nhỏ và giá trị lớn hơn một chút. So sánh thời gian đo và tính toán chi phí.

Cách chính xác nhất là hãy xem danh sách tháo gỡ, mã được tạo. Nhưng điều đó không dành cho người yếu tim. Bạn sẽ phải nghiên cứu kỹ bảng dữ liệu trong mỗi chu kỳ hướng dẫn mất bao lâu.


Làm thế nào bạn sẽ đo thời gian thực hiện bởi 1000 millis()cuộc gọi?
apnorton

Bạn có biết millis () được cung cấp bởi một ngắt trên timer0 làm tăng một biến nội bộ mỗi tích tắc không?
TheDoctor

@TheDoctor Tôi trộn lẫn với delay, bạn nói đúng. Nhưng ý tưởng vẫn giữ nguyên, bạn có thể thực hiện một số lượng lớn các cuộc gọi và tính trung bình cho chúng. Tuy nhiên, tắt các ngắt trên toàn cầu có thể không phải là một ý tưởng hay; o)
jippie

Đảm bảo rằng tập dữ liệu của bạn đủ lớn vì việc in các ký tự lên Nối tiếp mất vài mili giây. Tôi không nhớ thời gian chính xác, nhưng tôi nghĩ đó là khoảng ~ 0,6ms cho mỗi ký tự được gửi tới Sê-ri.
Steven10172

@ Steven10172 bạn có thể đặt chuỗi thời gian trống so với chuỗi 1000 lần (hoặc hơn), sau đó bạn biết đồng bằng và phép đo chính xác hơn.
jippie

3

Tôi gọi lần thứ hai millis nhiều lần và sau đó so sánh thực tế so với dự kiến.

Sẽ có một số chi phí tối thiểu, nhưng nó sẽ giảm đi đáng kể khi bạn gọi millis () nhiều lần hơn.

Nếu bạn nhìn vào

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

Bạn có thể thấy rằng millis () rất nhỏ chỉ với 4 hướng dẫn (cli is simply # define cli() \__asm__ \__volatile__ ("cli" ::))và trả về.

Tôi gọi nó là khoảng 10 triệu lần bằng cách sử dụng vòng lặp FOR có biến động là điều kiện. Từ khóa dễ bay hơi sẽ ngăn trình biên dịch thử bất kỳ tối ưu hóa nào trên chính vòng lặp.

Tôi không đảm bảo những điều sau đây là hoàn hảo về mặt cú pháp ..

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");

Tôi đoán là mất ~ 900ms hoặc khoảng 56us cho mỗi cuộc gọi đến millis. (Tôi không có máy ATM tiện dụng aruduino.


1
Bạn nên thay đổi int temp1,temp2;để volatile int temp1,temp2;ngăn trình biên dịch có khả năng tối ưu hóa chúng đi.
Sói Connor

Cuộc gọi tốt trên biến động. Tôi chắc chắn có ý định đưa nó vào và sau đó thì không. Tôi cũng nên đề cập rằng cách để làm một điểm chuẩn phù hợp hơn là chạy một vòng lặp trống, ghi lại thời gian thực hiện đó, sau đó chạy lại vòng lặp trong khi thực hiện công việc. Trừ đi sự khác biệt, chia cho số lần lặp và có thời gian thực hiện chính xác cao của bạn.
80HD

Loại điểm chuẩn đó chỉ hoạt động trên một hệ thống chưa từng thực hiện trước mã thực thi mã của bạn. Môi trường arduino theo mặc định có các ngắt định kỳ sẽ thực hiện định kỳ. Một giải pháp tốt hơn là chuyển đổi mã pin trên mỗi lần thực hiện và sử dụng bộ đếm thời gian có độ phân giải cao để đo tốc độ chuyển đổi khi chạy và không chạy mã được đề cập, hãy dành thời gian thực hiện tối thiểu cho một số mẫu cho mỗi lần thực hiện , trừ đường cơ sở và coi đó là thời gian thực hiện của bạn. Giả sử thời gian thực hiện của bạn ngắn hơn thì thời gian tối thiểu giữa các lần ngắt.
Sói Connor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.