关于c ++:Arduino – 通过指针传递struct似乎比按值慢

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

我写了一些伪代码来解释我在实际应用程序中发现的问题(arduino 1.6-https://github.com/maciejmiklas/leddisplay):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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

  • 执行测试方法:paint(...)—这将使用指针调用,如第211行所示。
  • 注释掉第211行,删除第212行的注释-从现在开始,测试将使用按值调用,执行时间将更短。

  • 代码的这一部分完全不起作用,优化器认识到:

    1
    2
    3
    4
    5
    6
    inline void Display::paintOnKitVal(KitData kd){
        for(int i = 0 ; i < 100 ; i++){
            kd.yDataIdx++;
            kd.yOnScreenIdx++;
        }
    }

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

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


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

    通过值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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
    }

    传递引用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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中。指针的更改将被反映并将是正确的。