【问题标题】:Arduino - passing struct by pointer seems to be slower than by valueArduino - 通过指针传递结构似乎比通过值慢
【发布时间】:2016-04-08 10:07:52
【问题描述】:

我编写了一些伪代码来解释我在实际应用程序中发现的问题(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++;
        .....
    }
}

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

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

这正常吗?

编辑:

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

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

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

【问题讨论】:

  • 我已经编辑了帖子 - 现在应该是正确的。
  • 你不是通过引用传递它,你传递的是一个指针。
  • 这段代码有很多问题。
  • 我是 C++ 新手 - 有什么问题?
  • 这个问题有一点语义问题:C++ 有实际的引用类型,其中 reference 会表示为 KitData&amp;。您传递的是一个指针,即KitData*,而不是一个引用。 C 总是按值传递参数,因此 C 程序员实际上传递指针以通过“引用”传递,但是当您谈论 C++ 时,调用该引用会令人困惑。

标签: c++ arduino


【解决方案1】:

这部分代码完全没有任何作用,优化器认识到:

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

您假设您测试了按值传递的性能。但是你确实测试了编译器识别代码什么都不做的能力。

当您通过指针传递时(C 程序员可能称之为“通过引用”但 C++ 程序员不会“通过引用”)不能说函数本身什么都不做。优化器需要更广泛地理解整个程序来检测缺乏效果。

【讨论】:

  • 这只是一个伪代码 - 真正的代码做得更多。
  • @MaciejMiklas 因为kd 是一个副本,所以编译器不会对它做任何事情。它不会被返回或引用。它是本地的,因此更改不会反映在此范围之外。编译器可以跳过所有这些代码。
  • 如果你的真实代码没有被优化器识别为什么都不做,它可能仍然被优化器识别为LESS,而不是指针传递版本。编译器知道您在按值传递对象中所做的任何更改都将在返回时被丢弃。
【解决方案2】:

传值和传引用的区别:

按值传递:

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
}

传递指针与传递引用类似,但有一些不同outlined here

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

对于小结构体,传值和传引用的速度会相似。 但是,内存占用会非常不同。每次传递某些东西时,按值传递都会进行复制,这将占用大量内存。

至于为什么更快:

There are likely optimizations 因为正在复制而不是对编译器为您进行的传入对象进行实际更改。然而,这是以不正确的算法为代价的。

传值后,变化不会反映在传入的kd中。指针的变化会被反映,并且是正确的。

【讨论】:

  • OP 发现按值传递比按引用传递快 30%。您对两者之间的区别进行了很好的解释,但您没有解释 OP 的结果。
  • 所以你的答案基本上是“优化”,没有任何迹象表明你可能在谈论什么样的优化。听起来您只是在猜测,而不是根据事实提供答案。
  • 没有理由跳到你的“算法不正确”的结论。由函数修改的对象的那些部分在函数退出后没有任何用途是完全合理的:在函数退出后的下一次使用之前,这些部分可能会被一致地重新修改。这非常适合内联传递值函数会揭示额外优化可能性的情况。
  • 这很有趣:playground.arduino.cc/Code/Pointer - “使用大于 8 位的数据类型时,通过引用传递 & 或 * 将提高性能。” - 我的结构至少有 16 个字节。所以......要么是我的测试错误,要么在传递结构时出现异常。
  • @MaciejMiklas 问题是您在循环中使用相同的kd 调用paintOnKitRef 5000 次,并且每次调用kd 时都会修改方法。因为您传递的是指向 kd 而不是副本的指针,所以这些更改仍然存在。举例说明:尝试单步执行两次对paintOnKitVal 的调用,然后对paintOnKitRef 执行相同的操作。在前一种情况下,两个调用将在kd 中具有相同的值;在后一种情况下,kd 中的值第二次会有所不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-28
  • 2017-01-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多