เป็นไปได้ไหมที่จะหาเวลาที่กำหนดโดยมิลลิวินาที?


13

ฟังก์ชันmillisจะทำงานในช่วง 100+ วินาทีหรือน้อยกว่า มีวิธีที่เชื่อถือได้ในการวัดเวลาที่ใช้ในการโทรหนึ่งพันครั้งหรือไม่?

วิธีการหนึ่งที่อยู่ในใจคือการใช้microsอย่างไรก็ตามการเรียกไปยังmicrosจะรวมเวลาที่ใช้ในการเรียกใช้ฟังก์ชันของmicrosตัวเองเช่นกันดังนั้นขึ้นอยู่กับระยะเวลาในการใช้ไมโครวินาทีการวัดสำหรับmillisอาจปิด

millisฉันต้องการที่จะพบนี้เป็นโปรแกรมที่ผมกำลังทำงานอยู่บนต้องมีการวัดเวลาที่ถูกต้องสำหรับทุกขั้นตอนการดำเนินการในรหัสรวมทั้ง


คุณช่วยอธิบายสิ่งที่คุณถามที่นี่ได้ไหม? คุณกำลังพยายามรับเวลาที่ถูกต้องจากมิลลิวินาที () หรือคุณกำลังพยายามหาระยะเวลาในการเรียกใช้ฟังก์ชันมิลลิวินาที ()
Cybergibbons

@Cybergibbons ใช้เวลาในการโทรนานmiilisเท่าใด
asheeshr

คำตอบ:


21

หากคุณต้องการทราบว่าต้องใช้เวลานานแค่ไหนมีวิธีแก้ปัญหาเดียวเท่านั้น: ดูที่การถอดแยกชิ้นส่วน!

เริ่มต้นด้วยรหัสขั้นต่ำ:

void setup(){};

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

}

รหัสนี้รวบรวมและป้อนเข้าสู่avr-objdump -Sเครื่องแล้วถอดแยกชิ้นส่วนที่ทำเป็นเอกสาร นี่คือข้อความที่ตัดตอนมาที่น่าสนใจ:

void loop() ผลิต:

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

ซึ่งเป็นการเรียกใช้ฟังก์ชัน ( call) สี่สำเนา (ซึ่งคัดลอกแต่ละไบต์ในuint32_tค่าส่งคืนของmillis()(โปรดทราบว่าเอกสาร arduino เรียกสิ่งนี้ว่า a longแต่ไม่ถูกต้องที่จะไม่ระบุขนาดของตัวแปรอย่างชัดเจน)) และสุดท้ายคือ ฟังก์ชั่นกลับมา

callต้องใช้ 4 รอบนาฬิกาและแต่ละรอบstsต้องใช้ 2 รอบดังนั้นเราจึงมีขั้นต่ำ 12 รอบสำหรับการใช้งานฟังก์ชั่น

ตอนนี้ให้ดูที่การถอดแยกชิ้นส่วนของ<millis>ฟังก์ชั่นซึ่งอยู่ที่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

อย่างที่คุณเห็นmillis()ฟังก์ชั่นนั้นค่อนข้างง่าย:

  1. in บันทึกการตั้งค่าอินเตอร์รัปต์ (1 รอบ)
  2. cli ปิดการขัดจังหวะ (1 รอบ)
  3. lds คัดลอกหนึ่งใน 4 ไบต์ของมูลค่าปัจจุบันของตัวนับ milli ลงในการลงทะเบียนชั่วคราว (รอบ 2 นาฬิกา)
  4. lds Byte 2 (2 รอบนาฬิกา)
  5. lds ไบต์ 3 (2 รอบนาฬิกา)
  6. lds ไบต์ 4 (2 รอบนาฬิกา)
  7. out เรียกคืนการตั้งค่าอินเตอร์รัปต์ (1 รอบนาฬิกา)
  8. movw ลงทะเบียนสับเปลี่ยนรอบ (1 รอบนาฬิกา)
  9. movw และอีกครั้ง (รอบ 1 นาฬิกา)
  10. ret กลับจากรูทีนย่อย (4 รอบ)

ดังนั้นถ้าเราเพิ่มพวกมันทั้งหมดลงไปเราก็จะมีวงจรนาฬิกา 17 รอบในmillis()ฟังก์ชันนั้นบวกด้วยค่าใช้จ่ายในการโทร 12 รวมเป็น 29 รอบนาฬิกา

สมมติว่าอัตรานาฬิกา 16 Mhz (arduinos ส่วนใหญ่) แต่ละรอบนาฬิกาเป็น1 / 16e6วินาทีหรือ 0.0000000625 วินาทีซึ่งเป็น 62.5 นาโนวินาที 62.5 ns * 29 = 1.812 microseconds

ดังนั้นเวลาดำเนินการรวมเป็นหนึ่งเดียวสำหรับmillis()การโทรในที่สุด Arduinos จะ1.812 ไมโคร


การอ้างอิง AVR Assembly

ในฐานะที่เป็นบันทึกด้านข้างมีพื้นที่สำหรับการเพิ่มประสิทธิภาพที่นี่! ถ้าคุณปรับปรุงunsigned long millis(){}นิยามฟังก์ชันที่จะเป็นinline unsigned long millis(){}คุณจะเอาค่าใช้จ่ายโทร (ที่ค่าใช้จ่ายของเล็กน้อยรหัสขนาดขนาดใหญ่) นอกจากนี้ดูเหมือนว่าคอมไพเลอร์กำลังทำการเคลื่อนไหวที่ไม่จำเป็นสองครั้ง (การmovwโทรสองครั้ง แต่ฉันไม่ได้มองอย่างใกล้ชิด)

จริงๆแล้วเมื่อพิจารณาถึงค่าใช้จ่ายในการเรียกฟังก์ชั่นคือ 5 คำแนะนำและเนื้อหาจริงของmillis()ฟังก์ชั่นนั้นมีเพียง 6 คำสั่งเท่านั้นฉันคิดว่าmillis()ฟังก์ชั่นควรจะเป็นinlineค่าเริ่มต้นจริง ๆ


นี่คือ disassemby เต็มรูปแบบสำหรับทุกคนที่สนใจ:

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>

ว้าวคำตอบที่ดี! +1
คนที่มีหมวก

1) ทั้งสี่stsไม่ควรนับเป็นค่าใช้จ่ายในการโทร: นี่คือค่าใช้จ่ายในการจัดเก็บผลลัพธ์ในตัวแปรระเหยซึ่งโดยปกติคุณจะไม่ทำ 2) ในระบบของฉัน (Arduino 1.0.5, gcc 4.8.2) ฉันไม่มีmovws ดังนั้นค่าใช้จ่ายในการโทรmillis()คือ: 4 รอบของค่าใช้จ่ายในการโทร + 15 รอบในmillis()ตัวเอง = รวม 19 รอบ (≈ 1.188 µs @ 16 MHz)
Edgar Bonet

1
@EdgarBonet - แต่นั่นไม่ได้ทำให้ความรู้สึกเป็นx uint16_tควรเป็น 2 ชุดมากที่สุดหากเป็นสาเหตุ อย่างไรก็ตามคำถามคือระยะmillis()เวลาที่ใช้เมื่อใช้ไม่ใช่เมื่อถูกเรียกขณะละเว้นผลลัพธ์ ตั้งแต่การใช้งานจริงใด ๆ volatileที่เกิดขึ้นจะเกี่ยวข้องกับการทำอะไรบางอย่างกับผลที่ฉันถูกบังคับผลที่ได้จะถูกเก็บไว้ผ่านทาง โดยปกติแล้วเอฟเฟกต์แบบเดียวกันนี้จะเกิดขึ้นได้จากการใช้ตัวแปรที่ตั้งค่าเป็นค่าส่งคืนของการโทรในภายหลัง แต่ฉันไม่ต้องการให้การโทรพิเศษนั้นใช้พื้นที่ว่างในคำตอบ
Connor Wolf

สิ่งนี้uint16_tในแหล่งที่มาไม่ตรงกับชุดประกอบ (4 ไบต์เก็บไว้ใน RAM) คุณอาจโพสต์แหล่งที่มาและแยกชิ้นส่วนของสองรุ่นที่แตกต่างกัน
Edgar Bonet

@ConnorWolf คำตอบและคำอธิบายที่น่าทึ่ง ขอขอบคุณ!
Lefteris

8

เขียนภาพร่างที่มีค่า 1,000 มิลลิวินาทีไม่ใช่โดยการวนซ้ำ แต่โดยการคัดลอกและวาง วัดและเปรียบเทียบกับเวลาที่คาดหวังจริง โปรดทราบว่าผลลัพธ์อาจแตกต่างกันไปตามรุ่นต่างๆของ IDE (และคอมไพเลอร์โดยเฉพาะ)

อีกทางเลือกหนึ่งคือการสลับ IO pin ก่อนและหลังการเรียก millis จากนั้นวัดเวลาสำหรับค่าที่น้อยมากและค่าที่ค่อนข้างใหญ่กว่า เปรียบเทียบการจับเวลาที่วัดได้และคำนวณค่าใช้จ่าย

วิธีที่ถูกต้องที่สุดคือการดูรายการถอดแยกชิ้นส่วนรหัสที่สร้างขึ้น แต่นั่นไม่ได้มีไว้สำหรับลมใจ คุณจะต้องศึกษาแผ่นข้อมูลอย่างละเอียดว่าแต่ละรอบการเรียนการสอนใช้เวลานานเท่าใด


คุณจะวัดเวลาที่ใช้ในการmillis()โทร1,000 ครั้งอย่างไร
apnorton

คุณรู้หรือไม่ว่า millis () นั้นมาจากการขัดจังหวะบน timer0 ซึ่งจะเพิ่มตัวแปรภายในทุกเห็บ?
TheDoctor

@TheDoctor ฉันผสมกับdelayคุณถูกต้อง แต่ความคิดยังคงเหมือนเดิมคุณสามารถโทรหาคนจำนวนมากและเฉลี่ยพวกเขา การปิดการขัดจังหวะทั่วโลกอาจไม่ใช่ความคิดที่ดีนัก; o)
jippie

ตรวจสอบให้แน่ใจว่าชุดข้อมูลของคุณมีขนาดใหญ่พอที่จะพิมพ์อักขระไปยังอนุกรมใช้เวลาสองสามมิลลิวินาที ฉันจำเวลาไม่แน่นอน แต่ฉันคิดว่ามันคล้ายกับ ~ 0.6ms ต่อตัวละครที่ส่งไปยังซีเรียล
Steven10172

@ Steven10172 คุณสามารถจับเวลาสตริงว่างกับสตริง 1000 ครั้ง (หรือมากกว่า) แล้วคุณจะรู้ว่าเดลต้าและการวัดมีความแม่นยำมากขึ้น
jippie

3

ฉันเรียกมิลลิวินาทีซ้ำแล้วเปรียบเทียบจริงกับที่คาดไว้

จะมีบางค่าใช้จ่ายน้อยที่สุด แต่จะลดลงอย่างมีนัยสำคัญยิ่งครั้งที่คุณเรียกมิลลิวินาที ()

ถ้าคุณดู

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

คุณจะเห็นว่า millis () มีขนาดเล็กมากเพียงแค่ 4 คำสั่ง(cli is simply # define cli() \__asm__ \__volatile__ ("cli" ::))และส่งคืน

ฉันจะเรียกมันว่าประมาณ 10 ล้านครั้งโดยใช้ FOR loop ซึ่งมีความผันผวนตามเงื่อนไข คำสำคัญระเหยจะป้องกันไม่ให้คอมไพเลอร์พยายามเพิ่มประสิทธิภาพใด ๆ ในวงเอง

ฉันไม่รับประกันว่าสิ่งต่อไปนี้จะสมบูรณ์แบบทางไวยากรณ์ ..

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

ฉันเดาว่าจะใช้เวลาประมาณ 900ms หรือประมาณ 56us ต่อการโทรหนึ่งพันครั้ง (ฉันไม่มีตู้เอทีเอ็มที่ใช้ง่ายอารุดดู


1
คุณควรเปลี่ยนint temp1,temp2;เป็นvolatile int temp1,temp2;เพื่อป้องกันไม่ให้คอมไพเลอร์อาจปรับให้เหมาะสม
Connor Wolf

โทรดีในความผันผวน ฉันตั้งใจจะใส่มันลงไปแล้วก็ไม่ได้ ฉันควรจะพูดถึงด้วยว่าวิธีการทำเบนช์มาร์กที่เหมาะสมกว่านั้นคือการวนลูปเปล่าบันทึกเวลาการประมวลผลนั้นจากนั้นรันลูปอีกครั้งขณะทำงาน ลบส่วนต่างหารด้วยจำนวนการวนซ้ำและมีเวลาดำเนินการที่แม่นยำสูงของคุณ
80HD

เกณฑ์มาตรฐานประเภทนี้ใช้งานได้บนระบบที่ไม่เคยทำการประมวลผลโค้ดของคุณล่วงหน้า สภาพแวดล้อม arduino โดยค่าเริ่มต้นมีการขัดจังหวะเป็นระยะที่จะดำเนินการเป็นระยะ ทางออกที่ดีกว่าคือการสลับพินในทุกการประมวลผลและใช้ตัวจับเวลาความละเอียดสูงของการเรียงลำดับบางอย่างเพื่อวัดอัตราการสลับเมื่อทำงานและไม่ได้รันโค้ดที่เป็นปัญหาใช้เวลาดำเนินการขั้นต่ำกับจำนวนตัวอย่างสำหรับแต่ละ ลบพื้นฐานและการรักษาที่เป็นเวลาปฏิบัติการของคุณ สมมติว่าเวลาดำเนินการของคุณสั้นลงดังนั้นเวลาต่ำสุดระหว่างการขัดจังหวะ
Connor Wolf
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.