小的重复算术与创建新变量

Small repeated arithmetic vs creating new variables

我正在用 C++ 编写低规格设备(~3MB RAM,~70MHz CPU),我想知道哪个 运行 更有效(以及多少)。这是一个简化的代码段,每秒 运行 120-600 次:

void checkCollisions(int x, int y)
{
    int p1x = x, p1y = y+2;
    int p2x = x, p2y = y+3;
    int p3x = x+3, p3y = y+3;
    // And so on...

    if (wallAt(p1x-1, p1y) || wallAt(p2x-1, p2y))
        setCollision(LEFT, true);
    if (wallAt(p1x, p1y) || wallAt(p4x, p4y) || wallAt(p5x, p5y))
        inGround = true;
    else
        inGround = false;
    // And so on...
}

或者用它们的定义替换整数:

void checkCollisionsAlt(int x, int y)
{
    if (wallAt(x-1, y+2) || wallAt(x-1, y+3))
        setCollision(LEFT, true);
    if (wallAt(x, y+2) || wallAt(x+3, y) || wallAt(x+2, y))
        inGround = true;
    else
        inGround = false;
    // And so on...
}

这是示例图:

第一个更容易理解,但我预计会占用更多内存。它有多大的不同?

如果机器以 70 MHz 运行,这意味着它每 600 秒有 117,000 个周期。

如果每条指令平均需要 10 个周期,则它可以在 600 秒内执行 11,700 条指令。

当我查看您的代码时,我估计大约需要 100 条指令来执行它。 100/11,700 = 大约花费 1% 的时间 运行 此代码。

您可以在汇编语言级别逐步执行它以查看需要多少条指令,但这可能不会有太大区别。

我怀疑你在别处有更大的鱼要炸。

思考的几点:

  1. 如果完整版也没有任何递归,你可以不用担心堆栈上的变量(p1x等)。
  2. 堆栈消耗是短暂的,除非您有病态代码,例如深度递归,每一帧都很重,否则不会影响您。
  3. 在内存预算紧张的情况下,递归通常不是一个好主意。
  4. 您将它们作为明确命名的变量这一事实并不意味着它们在执行时会如此。
  5. 任何体面的编译器都可能识别变量的生命周期并将它们推送到寄存器。请使用您当前使用的编译器优化级别验证这一点,并在需要时考虑提高它。

此外,p1x 等这些值的预期范围是多少。为什么不使用 short int?

堆栈内存增长的短暂性意味着您的峰值堆内存不会受到影响。堆栈可以增长和缩小,具体取决于堆栈的布局和预算金额,您可能根本不必担心这一点。

注意:任何和所有堆分配都需要仔细审查。尝试实现自定义分配器,而不是产生标准的 malloc() 块开销。当然,您没有在问题中提到堆,但是,请记住这一点。

如果编译器按您希望的方式运行,将使您的代码更快的方法是将所有变量放入寄存器。我认为任何现代编译器都会从自身理解您的 2 个代码版本是相同的,并且会给出相同或非常相似的输出。在这两种情况下,它都会尝试使用核心寄存器,并且只有在没有足够数量的寄存器可用时才会使用内存(在本例中为堆栈)。如果编译器为您提供了保留中间汇编文件的选项,那么您可以深入了解您的代码和性能。请记住,低内存访问 - 改用寄存器 - 将提高您的代码性能。