2014-09-28 1 views
0

У меня есть программа для avr, где я бы хотел использовать указатель на метод. Но почему использование указателя функции над обычным вызовом почти в 4 раза медленнее? И как мне это ускорить?Ускорение указателей функций AVR

у меня есть:

void simple_call(){ PORTB |= _BV(1); } 

void (*simple)() = &simple_call; 

Тогда, если я компилирую с -O3 и называют:

simple_call() 

он принимает 250ns, чтобы закончить. Если я позвоню:

simple() 

Требуется 960ns для завершения!

Как это сделать быстрее?

ответ

6

Почему это медленнее ??

Вы видите увеличение на 710 нс. Для часов 16 МГц это время составляет 11 тиков.

Это не очень справедливо сказать 4X, потому что увеличение времени является постоянным накладным для функции указателя. В вашем случае тело функции крошечное, поэтому накладные расходы относительно велики. Но если бы у вас был случай, когда функция была большой и заняла 1 мс для выполнения, увеличение времени все равно было бы 710 нс, и вы спрашивали, почему указатель функции занимает более 0,07%?

Чтобы понять, почему один подход быстрее, чем другой, вам нужно получить код ассемблера. Используя инструменты построения, такие как Eclipse, вы можете получить список ассемблеров из компилятора GCC, добавив параметры командной строки, недоступные с помощью Arduino IDE. Это бесценно, чтобы выяснить, что происходит.

Вот часть ассемблере листинг показывает, что вы думаете, что происходит:

simple_call(); 
    308: 0e 94 32 01  call 0x264 ; 0x264 <_Z11simple_callv> 

simple(); 
    30c: e0 91 0a 02  lds r30, 0x020A 
    310: f0 91 0b 02  lds r31, 0x020B 
    314: 19 95   eicall 

Эти списки показывают, исходный код и ассемблер, полученный компилятором. Чтобы понять это и выяснить время, вам нужен Atmel AVR instruction reference, который содержит описания каждой инструкции и количество тактов, которые они принимают. Простой_call(), возможно, то, что вы ожидаете, и занимает 4 тика. Простой() говорит:

LDS = load address byte - 2 ticks 
LDS = load address byte - 2 ticks 
EICALL = indirect call to address loaded - 4 ticks 

Те как вызвать функцию simple_call():

void simple_call(){ PORTB |= _BV(1); } 
264: df 93   push r29 
266: cf 93   push r28 
268: cd b7   in r28, 0x3d ; 61 
26a: de b7   in r29, 0x3e ; 62 
26c: a5 e2   ldi r26, 0x25 ; 37 
26e: b0 e0   ldi r27, 0x00 ; 0 
270: e5 e2   ldi r30, 0x25 ; 37 
272: f0 e0   ldi r31, 0x00 ; 0 
274: 80 81   ld r24, Z 
276: 82 60   ori r24, 0x02 ; 2 
278: 8c 93   st X, r24 
27a: cf 91   pop r28 
27c: df 91   pop r29 
27e: 08 95   ret 

Таким образом, указатель на функцию должен взять только 4 больше тиков и мал по сравнению со всеми инструкциями в функции метод.


Выше я сказал РЕКОМЕНДУЕМЫМ и , что вы думаете, что происходит. Я немного солгал: ассемблер выше не оптимизирует.

Вы использовали оптимизацию -O3, которая меняет все.

С оптимизаций, функция получает тело сжато почти ничего:

void simple_call(){ PORTB |= _BV(1); } 
264: 29 9a   sbi 0x05, 1 ; 5 
266: 08 95   ret 

То есть 2 + 4 тиков. Гуру-компилятор закодировал компилятор, чтобы выяснить гораздо лучший способ выполнить одну строку C++. Но ждать больше. Когда вы «вызываете» свою функцию, компилятор говорит «зачем это? Это всего лишь одна инструкция ассемблера». Компилятор решает ваш вызов бессмысленно и ставит инструкции инлайн:

void simple_call(){ PORTB |= _BV(1); } 
2d6: 29 9a   sbi 0x05, 1 ; 5 

Но с оптимизациями, вызов указатель функции остается вызов:

simple(); 
2d8: e0 91 0a 02  lds r30, 0x020A 
2dc: f0 91 0b 02  lds r31, 0x020B 
2e0: 19 95   eicall 

Так что давайте посмотрим, если математика складывает. С встроенным, «звонок» - 3 тика. Косвенный вызов равен 8 + 6 = 14. Разница - 11 тиков! (Могу добавить!)

Так что это ** почему *.

Как ускорить его?

Вам не нужно: Только 4 тиков больше, чтобы сделать вызов указателя функции. За исключением самых тривиальных функций, это не имеет значения.

Вы не можете: Даже если вы попытаетесь встроить функции, вам все равно нужна условная ветвь. Букет нагрузок, сравнения и условных прыжков займет больше, чем косвенный вызов. Другими словами, указатель функции - лучший метод ветвления, чем любой условный.