如果程序中没有结构,C ++代码是否运行得更快

时间:2012-03-17 15:40:13

标签: c++

我知道如果我们使用类,结构等构建我们的程序会有很大的帮助,但它在运行速度方面是否有帮助我们避免使用这些结构并根据基本的C ++语法编写代码?

例如,我正在尝试编写一个适用于矢量的程序。现在,编写一个类向量并定义像set_at_index(int i)这样设置该向量的特定行i的值的方法听起来很诱人。此外,我可以检查i<=N N是否是所讨论的向量的长度。

我的困惑在于,使用这些例程的每个set_at_index方法都需要一个'if'语句。因此,如果我希望我的代码运行得更快,我应该避免它并继续声明一个数组并手动注意没有内存泄漏?

有没有什么方法可以检查内存泄漏而不会给代码速度带来负担?

5 个答案:

答案 0 :(得分:6)

是的,边界检查会花费更多时间。但是,如果代码运行28894389375次,那么它将花费很少的额外时间,然后它可能会加起来一毫秒。请注意,如果您使用std::vector成员函数,at仅执行边界检查,而不是使用operator[]。此外,如果您正在执行任何操作,例如写入文件或将文本打印到控制台,那么执行该操作可能需要花费的时间超过一千万个边界检查数组访问,因为I / O相对非常慢。

通常,没有边界检查使用类的代码将以与使用普通数组的代码相同的速度运行。像你建议的那样手动管理内存的问题是很容易忘记清理它,或者只通过程序中的一条执行路径来清理它,或者在发生异常时无法清理它。这真的很不值得。此外,使用没有边界检查的矢量类也同样快,因为它将使用没有边界检查的动态数组。无论如何都要付钱。

我还建议使用std::vector而不是编写自己的矢量类,因为它们几乎可以完成您自己可以完成的所有优化,并且它们通常具有能够为其特定编译器编写代码的优势。能够利用只有编译器所做的事情,因为他们更了解它的实现。 STL课程也经过专家(通常)的严格测试和编写。

您应首先编写代码,然后使用分析器进行衡量,以查看代码中的瓶颈如果还不够快,则优化瓶颈。我敢打赌,检查数组的边界可能不会成为这些瓶颈之一。

可以使用valgrind之类的工具检查内存泄漏。你不要在代码本身中这样做。

答案 1 :(得分:1)

在开始写作之前不要试图过度优化。继续编写易于维护,可读且尽可能无错误的代码。一旦你有工作,你可以开始分析,看看真正的瓶颈。

答案 2 :(得分:0)

Premature optimization is root of all evils” - 唐纳德克努特。 (这是97%的时间)。

除非您对应用程序进行概要分析并发现您的类封装是一个瓶颈,确实会大大减慢您的应用程序,否则请不要犹豫,建立高级别的结构。它将为您带来诸多好处,如可读性,维护性和理解您的工作。这就是带来OOP:大规模计划的原因。

答案 3 :(得分:0)

两件事:

  • 不要做任何过早的优化,现在CPU速度很快,编译器很聪明,能够找出在查看代码的几个月内你都不会想到的优化。
  • 您可以通过分析代码和/或使用条件编译轻松检查内存泄漏等内容。在发布版本上不应发生泄漏,因此您应该跳过该检查。

答案 4 :(得分:0)

已经发布了一些好的答案,并且正如其他人所说的那样,过早的优化确实是不可取的。但是,让我再提一下你的问题。

  

我知道如果我们使用类构建程序会有很大帮助,   结构等,但它有助于我们避免的运行速度   这些结构和编写代码基本上是基本的C ++语法吗?

理论上,大多数正确编写的C ++代码应该与完全开发的类一样快,但没有,但是

  1. 规则有例外;
  2. 理论上正确编写C ++代码所需的工作量可能太大;和
  3. C ++编译器的相同功能使得编写错误的代码变得很困难,这使得编写非常低效的代码变得非常容易。
  4. 逐点说明。

    1. 考虑复杂的三维矢量类型,其中每个实例由六个双精度(三个实部和三个虚部)组成。如果没有那么多双打,你的编译器可能会将它们直接加载到你的微处理器的寄存器中,但是当加载复杂的三维向量时,它们可能会有6个被保留在堆栈中。然而,在复杂的三维矢量上的一些操作不需要全部六个双打,而是仅需要一个,两个或三个。如果是这样,则最好分别存储六个浮点组件。因此,不是1000个向量的数组,而是保留六个1000个双精度数组。当然,可以(并且可能应该)将数组绑定在某种类中,但是 - 仅出于效率原因 - 一个好的设计可能永远不会明确地将一个数组中的各个元素关联到另一

    2. 有时,您知道您的数据在哪里以及您想要用它做什么,而C ++精心设计的组织和访问控制设施只会妨碍您。在这种情况下,您可以跳过高级C ++,然后在原始的,hackworthy,野蛮的,使用大砍刀的C风格代码中执行您想要的操作。实际上,C ++明确地支持这种编码风格,它使得原始C代码可以安全地包装在一个模块中,从而使其隐藏在你漂亮的C ++程序的其余部分中。当然,如果你把你的代码交给一把砍刀,可以这么说,那么你就冒了风险而不是你,因为你的代码可能会破坏你从未想过的数据,而你的编译器会站在一边让它做到但有时风险是值得的,有时风险甚至是程序员改变节奏的乐趣(和性格建设)。

    3. 这一点是三者中最微妙的。如果用户定义的类型部分由其他用户定义的类型组成,则将调用并隐式调用多个构造函数层。这很好,通常它就是你想要的,特别是如果你在每一层都有一个好的单元测试机制。然而玫瑰却有一根刺。一个正确编写的构造函数小心永远不会丢失它需要的任何东西。因此,除非程序员最小心,否则构造函数可能会悄悄地制作大量非常大的对象的严格不必要的副本。有时,程序员会精神上失去对隐式调用的所有级别的跟踪,如果他必须明确地处理每个调用,他就不会这样做。此外,您在一种类型的对象中的数据可能无法访问它可以轻松访问的成员函数,只要它暂时将自身复制到另一种类型的对象(您可以使用句柄类型避免复制) ,引用计数等等,但这不是免费的:这是相当多的工作)。即使程序员意识到隐式副本,隐藏副本也很容易编码,因为这样做的诱惑有时太大了 - 特别是当截止日期临近时!在这些方面可能会出现一些隐藏的低效率。当然,人们可以而且应该解决这种效率低下的问题,但是这样做可能需要大量的编码工作,即使这样,您的编译器也会忙于帮助您避免逻辑错误它往往会导致你创造出你从未故意创造的无意中的低效率。对C ++进行不必要的,隐藏的数据复制是一个比C中更大的问题。

    4. 总而言之,我会说C ++的权衡在80%的时间里是值得的。 C ++的组织和访问控制设施值得正确应用它们所需的努力。如果你的问题是20%,那么,在我看来,有不止一种有效的编程方法。有时它确实有帮助我们避免使用这些结构并根据基本的C ++语法编写代码,&#34;如你所说。

      通常,没有。有时候是。我认为前面的答案是对的,你提出的具体例子可能更好地用无聊,整洁,有序的C ++来处理,没有技巧。