c++ - 自定义 C++ 类可以复制内置类型的性能吗?

标签 c++ embedded stack operator-overloading

我正在尝试创建一个行为与内置 int 类型完全相同的 C++ 类,但有一个异常(exception):在调用 operator*(或 operator*=)的任何地方,都调用了加法。

起初,我的类的性能很差(是内置 int 类型的 1/2),但我注意到这是因为我忘记包含下面的复制构造函数:

struct AlmostInt {                                                                                                                                                                       

  AlmostInt () { }                
  AlmostInt (const AlmostInt  &a) : val(a.val) { }  // forgetting this killed
                                                    // performance

  AlmostInt operator+(const AlmostInt &a) const { AlmostInt result = *this;
                                          result.val += a.val;
                                          return result; }
  AlmostInt operator-(const AlmostInt &a) const { AlmostInt result = *this;
                                          result.val -= a.val;
                                          return result; }
  AlmostInt operator*(const AlmostInt &a) const { AlmostInt result = *this;
                                          result.val  = result.val + a.val;      
                                          return result; }
  AlmostInt &operator+=(const AlmostInt &a) { this->val += a.val;                           
                                              return *this; }
  AlmostInt &operator-=(const AlmostInt &a) { this->val -= a.val;        
                                              return *this; }
  AlmostInt &operator*=(const AlmostInt &a) { this->val = this->val + a.val);     
                                              return *this; }

private:
  int val;
};

不幸的是,我的程序仍然比应有的速度慢 25%。检查为程序的两个不同版本生成的程序集(一个使用 int,另一个使用 AlmostInt),我发现 + 和 - 操作的数量相同,所以事情在某种程度上是“有效的”。

问题是使用 AlmostInt 类而不是原生 int 操作的代码中有更多的加载和存储操作。

有没有人知道这个开销可能来自哪里?唯一的猜测 我曾经是,也许编译器不明白 AlmostInt 有所有 int 具有相同的属性(例如关联性、交换性),但如果这是真的 一个问题,我本来希望代码中有不同数量的“+”或“-”指令,但这不会发生。

我怀疑额外的加载和存储与额外的堆栈事件有关,但是 在这一点上我只能说这不仅仅是一些额外的堆栈加载和存储在 每个函数的顶部和底部,但额外的加载和存储发生在整个代码中。

有什么想法吗?我想知道是否有人可以向我指出 允许 允许的编译器 使用自定义类达到 int 的性能水平。

更新:

这是一个简单的函数,您可以剪切和粘贴以查看自己发生了什么。在 x86-64 Linux (g++ 4.3, 4.4)、AIX6 xlC 和其他几个平台上,更改下面的“CHOOSE ONE...”行应该会导致生成相同的代码(或在相同性能的最少代码),但在实践中代码显着膨胀。谁能解释发生了什么(对于任何特定平台/编译器),或者如何解决它?

class AlmostInt
{
    int value;

public:

    AlmostInt& operator+=(AlmostInt that)
    {
        value += that.value;
        return *this;
    }

    AlmostInt& operator-=(AlmostInt that)
    {
        value -= that.value;
        return *this;
    }

        AlmostInt& operator*=(AlmostInt that)
    {
        value *= that.value;
        return *this;
    }
};

AlmostInt operator+(AlmostInt lhs, AlmostInt rhs)
{
    lhs += rhs;
    return lhs;
}

AlmostInt operator-(AlmostInt lhs, AlmostInt rhs)
{
    lhs -= rhs;
    return lhs;
}

AlmostInt operator*(AlmostInt lhs, AlmostInt rhs)
{
    lhs *= rhs;
    return lhs;
}

// CHOOSE ONE OF THE FOLLOWING TWO LINES:
//typedef int real;
typedef AlmostInt real;

typedef struct {
  real re;
  real im;
} complex;

#define R(a0,a1,b0,b1,wre,wim) { \
  t1 = a0 - a1;  t2 = b0 - b1; \
  t5 = t1 * wim; t6 = t2 * wim; \
  t3 = a0;  t1 *= wre; \
  t3 += a1; t2 *= wre; \
  t1 -= t6; t4 = b0; \
  t2 += t5; t4 += b1; \
  a0 = t3;  b1 = t2; \
  a1 = t4;  b0 = t1; \
}

#define RZERO(a0,a1,b0,b1) { \
  t1 = a0 - a1; t2 = b0 - b1; \
  t3 = a0 + a1; t4 = b0 + b1; \
  b0 = t1; a0 = t3; \
  b1 = t2; a1 = t4; \
}

void rpass(real *a, const complex *w, unsigned int n)
{
  real t1, t2, t3, t4, t5, t6, t7, t8;
  real *b;
  unsigned int k;

  b = a + 4 * n;
  k = n - 2;

  RZERO(a[0],a[1],b[0],b[1]);
  R(a[2],a[3],b[2],b[3],w[0].re,w[0].im);
  R(a[4],a[5],b[4],b[5],w[1].re,w[1].im);
  R(a[6],a[7],b[6],b[7],w[2].re,w[2].im);

  for (;;) {
    R(a[8],a[9],b[8],b[9],w[3].re,w[3].im);
    R(a[10],a[11],b[10],b[11],w[4].re,w[4].im);
    R(a[12],a[13],b[12],b[13],w[5].re,w[5].im);
    R(a[14],a[15],b[14],b[15],w[6].re,w[6].im);
    if (!(k -= 2)) break;
    a += 8;
    b += 8;
    w += 4;
  }
}

(信用到期:这个小基准来自 Dan Bernstein 的 'djbfft' 库)

最佳答案

在这类情况下,性能下降的最常见原因之一 从函数返回值。理论上,编译器应该是 能够对此进行优化,并执行与返回 int 相同的操作 (前提是所有相关函数都是内联的);在实践中,所有 我知道的编译器会在寄存器中返回一个 int ,但是对于一个类 类型,将传递一个带有地址的附加隐藏参数 临时的,并在此地址返回内存中的值。原因 是像复制构造函数或赋值这样的东西需要一个 地址(this 指针,对正在复制的内容的引用),以及 编译器似乎没有认识到,一旦它内联了所有 功能,地址将不再是必需的。 (还有 事实上二进制 API 说要这样做,但是二进制 API 通常只涉及结构,而不涉及非平凡的类型 构造函数、析构函数和赋值运算符。)

关于c++ - 自定义 C++ 类可以复制内置类型的性能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6328290/

相关文章:

c++ - Eigen3 replicate() 用于矩阵 vector cwiseProduct 操作

c - 如何将 u32_t 中的十六进制值转换为其相应的 char/ascii 值?

embedded - 微 Controller 之间处理器间通信的 UART、I2C 与 SPI

amazon-web-services - Terraform - 一种集中状态或多种模块化状态

c++ - 链接堆栈模板和一堆错误

C++模板函数重载

c++ - 当用户在 Visual C++ 或代码块中输入回车时打破循环

c++ - 包含 boost binary_oarchive.hpp 时遇到问题

linux - ArchLinux 上的 I2C 用户空间通信

c# - MichaelJordan 在 .NET 堆栈上创建了多少个 ValueType 实例?