আপনি যদি ঠিক কতটা সময় নেবেন তা জানতে চান , তবে কেবল একটিই সমাধান রয়েছে: দেখেন নির্জনে!
সর্বনিম্ন কোড দিয়ে শুরু:
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()
(নোট করুন যে আরডুইনো ডক্স এটিকে কল করে 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()
ফাংশনটি মোটামুটি সহজ:
in
বাধা নিবন্ধক সেটিংস সংরক্ষণ করে (1 চক্র)
cli
বাধা বন্ধ করে (1 চক্র)
lds
অস্থায়ী রেজিস্টারে মিল কাউন্টারের বর্তমান মানের 4 বাইটের একটি অনুলিপি করুন (2 ঘড়ির চক্র)
lds
বাইট 2 (2 ঘড়ি চক্র)
lds
বাইট 3 (2 ঘড়ির চক্র)
lds
বাইট 4 (2 ঘড়ি চক্র)
out
বাধা সেটিংস পুনরুদ্ধার করুন (1 ঘড়ি চক্র)
movw
এলোমেলো করে রেজিস্টারগুলি প্রায় (1 ঘড়ির চক্র)
movw
এবং আবার (1 ঘড়ির চক্র)
ret
সাবরুটিন থেকে ফিরে (4 চক্র)
সুতরাং, আমরা যদি এগুলি সমস্ত যুক্ত করি তবে আমাদের কাছে ফাংশনটিতে মোট 17 টি ঘড়ির চক্র রয়েছে millis()
, এবং মোট 29 টি ঘড়ির চক্রের জন্য 12 এর একটি ওভারহেড কল রয়েছে।
একটি 16 মেগাহার্জ ক্লক রেট (সর্বাধিক আরডুইনো) ধরে নেওয়া, প্রতিটি ঘড়ি চক্রটি 1 / 16e6
সেকেন্ড বা 0.0000000625 সেকেন্ড, যা 62.5 ন্যানোসেকেন্ড। 62.5 এনএস * 29 = 1.812 মাইক্রোসেকেন্ড।
অতএব, বেশিরভাগ আরডুইনোতে একক millis()
কল করার জন্য মোট মৃত্যুর সময়কাল হবে 1.812 মাইক্রোসেকেন্ড ।
এভিআর বিধানসভা রেফারেন্স
পার্শ্ব-নোট হিসাবে, এখানে অপ্টিমাইজেশনের জন্য জায়গা রয়েছে! আপনি যদি unsigned long millis(){}
ফাংশন সংজ্ঞাটি আপডেট করতে চান তবে আপনি inline unsigned long millis(){}
কল ওভারহেড সরিয়ে ফেলবেন ( কিছুটা বড় কোড আকারের দামে )। তদুপরি, দেখে মনে হচ্ছে সংকলকটি দুটি অপ্রয়োজনীয় চালচলন করছে (দুটি movw
কল, তবে আমি এটি এত কাছ থেকে দেখিনি)।
সত্যিই, ফাংশন কল ওভারহেড বিবেচনায় 5 নির্দেশাবলী, এবং প্রকৃত বিষয়বস্তু এর millis()
ফাংশন মাত্র 6 নির্দেশাবলীর, আমি মনে করি millis()
ফাংশন আসলেই করা উচিৎ inline
ডিফল্টরূপে, কিন্তু যাও Arduino কোডবেস বরং দুর্বল অপ্টিমাইজ করা হয়।
আগ্রহীদের জন্য এখানে সম্পূর্ণ বিচ্ছিন্নতা রয়েছে:
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>