Arduino 加速AVR函数指针
我有一个avr的程序,我想使用一个指向方法的指针。但是为什么使用函数指针比普通调用慢4倍呢??我该如何加速呢 我有:Arduino 加速AVR函数指针,arduino,avr,atmega,Arduino,Avr,Atmega,我有一个avr的程序,我想使用一个指向方法的指针。但是为什么使用函数指针比普通调用慢4倍呢??我该如何加速呢 我有: void simple_call(){ PORTB |= _BV(1); } void (*simple)() = &simple_call; 然后,如果我使用-O3编译并调用: simple_call() 需要250纳秒才能完成。如果我改打电话: simple() 需要960ns才能完成 我怎样才能使它更快 为什么它慢了 你可以看到时间增加了710纳秒。对于
void simple_call(){ PORTB |= _BV(1); }
void (*simple)() = &simple_call;
然后,如果我使用-O3编译并调用:
simple_call()
需要250纳秒才能完成。如果我改打电话:
simple()
需要960ns才能完成
我怎样才能使它更快
为什么它慢了
你可以看到时间增加了710纳秒。对于一个16兆赫的时钟,这个时间是11个滴答声
说4X是不公平的,因为时间的增加对于函数指针来说是一个恒定的开销。在您的例子中,函数体很小,因此开销相对较大。但是,如果函数很大,执行时间为1ms,那么时间增加仍然是710ns,并且您会问为什么函数指针要多花费0.07%的时间
要了解为什么一种方法比另一种方法快,您需要了解汇编代码。使用Eclipse之类的构建工具,您可以通过添加ArduinoIDE中不可用的命令行选项,从GCC编译器中获取汇编程序列表。这对于弄清发生了什么是非常宝贵的
下面是汇编程序清单的一部分,展示了您认为正在发生的事情:
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
它们都调用函数simple\u call():
因此,与函数方法中的所有指令相比,函数指针应该只多取4个刻度,并且应该很小
上面,我说应该和你认为正在发生的事情。我撒了一点谎:上面的汇编程序不支持优化 您使用的优化-O3改变了一切 通过优化,函数体几乎被压缩为零:
void simple_call(){ PORTB |= _BV(1); }
264: 29 9a sbi 0x05, 1 ; 5
266: 08 95 ret
这是2+4个刻度。编译器GurUS已经对编译器进行编码,以找出执行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个时钟点。除了最琐碎的函数外,这并不重要
您不能:即使尝试内联函数,您仍然需要条件分支。一系列加载、比较和条件跳转将比间接调用花费更多的时间。换句话说,函数指针是比任何条件分支更好的分支方法
为什么它慢了
你可以看到时间增加了710纳秒。对于一个16兆赫的时钟,这个时间是11个滴答声
说4X是不公平的,因为时间的增加对于函数指针来说是一个恒定的开销。在您的例子中,函数体很小,因此开销相对较大。但是,如果函数很大,执行时间为1ms,那么时间增加仍然是710ns,并且您会问为什么函数指针要多花费0.07%的时间
要了解为什么一种方法比另一种方法快,您需要了解汇编代码。使用Eclipse之类的构建工具,您可以通过添加ArduinoIDE中不可用的命令行选项,从GCC编译器中获取汇编程序列表。这对于弄清发生了什么是非常宝贵的
下面是汇编程序清单的一部分,展示了您认为正在发生的事情:
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
它们都调用函数simple\u call():
因此,与函数方法中的所有指令相比,函数指针应该只多取4个刻度,并且应该很小
上面,我说应该和你认为正在发生的事情。我撒了一点谎:上面的汇编程序不支持优化 您使用的优化-O3改变了一切 通过优化,函数体几乎被压缩为零:
void simple_call(){ PORTB |= _BV(1); }
264: 29 9a sbi 0x05, 1 ; 5
266: 08 95 ret
这是2+4个刻度。编译器GurUS已经对编译器进行编码,以找出执行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个时钟点。除了最琐碎的函数外,这并不重要
您不能:即使尝试内联函数,您仍然需要条件分支。一系列加载、比较和条件跳转将比间接调用花费更多的时间。换句话说,函数指针是比任何条件分支更好的分支方法