Arduino-通过指针传递结构似乎比通过值传递慢

Arduino - passing struct by pointer seems to be slower than by value

本文关键字:值传 结构 指针 Arduino-      更新时间:2023-10-16

我写了一些伪代码,应该可以解释我在实际应用程序中发现的问题(Arduino 1.6-https://github.com/maciejmiklas/LEDDisplay):

Display.h:
class Display {
public:
    void testRef();
    void testVal();
private:
    typedef struct {
            uint8_t xOnFirstKit;
            uint8_t yOnFirstKit;
            uint8_t xRelKit;
            uint8_t yRelKit;
            uint8_t xRelKitSize;
            uint8_t yRelKitSize;
            uint8_t xDataBytes;
            uint8_t xKit;
            uint8_t yKit;
            uint8_t xOnKit;
            uint8_t yOnKit;
            uint8_t xOnKitSize;
            uint8_t yOnKitSize;
            uint8_t xOnScreenIdx;
            uint8_t yOnScreenIdx;
            uint8_t yDataIdx;
        } KitData;
 inline void paintOnKitRef(KitData *kd); 
 inline void paintOnKitVal(KitData kd); 
}

Display.cpp:
#include "Display.h"
void Display::testRef(){
    KitData *kd = ....
    for(int i = 0 ; i < 5000 ; i++){
       paintOnKitRef(kd);
       ....
    }
}
void Display::testVal(){
    KitData *kd = ....
    for(int i = 0 ; i < 5000 ; i++){
       paintOnKitVal(*kd);
       ....
    }
}
inline void Display::paintOnKitRef(KitData *kd){
    for(int i = 0 ; i < 100 ; i++){
        kd->yDataIdx++;
        kd->yOnScreenIdx++;
        .....
    }
}
inline void Display::paintOnKitVal(KitData kd){
    for(int i = 0 ; i < 100 ; i++){
        kd.yDataIdx++;
        kd.yOnScreenIdx++;
        .....
    }
}

我有一个大于16字节的结构:KitData,所以我决定通过指针而不是通过值来传递它——它按预期工作。

我测量了执行时间,看起来传递值(testVal())比传递引用(testRef())快30%左右。

这正常吗?

编辑:

上面的代码只是一个伪代码——在我的真实测试方法中:paintOnKitVal()paintOnKitRef()包含执行许多操作和其他方法的真实代码。这两种方法也做相同的事情——唯一的区别是访问kd(槽指针或点表示法)的方式。

这是真正的测试类:https://github.com/maciejmiklas/LEDDisplay/blob/callByPoint/Display.cpp

  1. 执行测试方法:paint(...)-这将使用指针调用,正如您在第211行中看到的那样
  2. 注释掉第211行并删除第212行的注释-从现在起,测试将使用按值调用,执行时间将更短

您的代码的这一部分完全不起任何作用,优化器会识别出:

inline void Display::paintOnKitVal(KitData kd){
    for(int i = 0 ; i < 100 ; i++){
        kd.yDataIdx++;
        kd.yOnScreenIdx++;
    }
}

您可以想象您测试了pass-by-value的性能。但您确实测试了编译器识别代码什么都不做这一事实的能力。

当你通过指针(C程序员可能会称之为"引用",但C++程序员不会称之为"引用")时,不能说函数本身什么都不做。优化器需要对整个程序有更广泛的了解,才能检测出缺乏效果。

按值传递和按引用传递之间的差异:

传递值:

void foo(int a) {
  a = 30; // passed in param is now 30 until end of scope
}
int main() {
  int b = 3;
  foo(b); // copy of b is made, copy is assigned value 30
  // b is still 3
}

通过参考:

void foo(int& a) {
  a = 30; // passed in param is now 30 because a reference was passed in
}
int main() {
  int b = 3;
  foo(b); // reference to b is assigned value 30
  // b is now 30
}

传递指针与通过引用传递类似,这里列出了一些区别。

您为testVal编写的代码将对kd的副本执行操作。这不是你想要的。

对于小型结构,按值传递和按引用传递的速度将相似但是,内存占用空间将大不相同。传递值将在每次传递某个内容时生成副本,这将占用大量内存。

至于为什么更快:

可能存在优化,因为正在对编译器为您生成的传入对象进行复制,而不是实际更改。然而,这是以错误的算法为代价的。

传递值后,更改将不会反映在传入的kd中。指针的更改将反映出来,并且是正确的。