如果程序中没有结构,c++代码运行得更快吗?< / h1 >

Does C++ code run faster if there is no structure in program

本文关键字:gt lt h1 程序 结构 运行 代码 c++ 如果      更新时间:2023-10-16

我知道如果我们使用类、结构体等来构建我们的程序会很有帮助,但是如果我们避免使用这些结构体,按照基本的c++语法来编写代码,这对提高运行速度有帮助吗?

例如,我想写一个处理向量的程序。现在,编写一个类向量并定义它的方法(如set_at_index(int i))听起来很诱人,它设置这个向量的特定行i的值。此外,我可以检查i<=N,其中N是所讨论的向量的长度。

我的困惑是,这些例程的每一个set_at_index方法是使用很多将需要一个'if'语句。所以,如果我想让我的代码运行得更快,我应该避免它,去声明一个数组,并手动注意没有内存泄漏吗?

是否有任何方法可以检查内存泄漏而不增加代码速度的负担?

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

通常,没有边界检查的代码使用类将以相同的速度运行代码使用普通数组。像您建议的那样手动管理内存的问题是,很容易忘记清理内存,或者只通过程序的一条执行路径清理内存,或者在发生异常时无法清理内存。这真的不值得。而且,使用没有边界检查的向量类和使用没有边界检查的动态数组一样快。不管怎样,你都得付出代价。

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

你应该先写代码,然后用分析器测量查看代码中的瓶颈,如果它还不够快,然后优化瓶颈。我敢打赌,数组的边界检查可能不会成为这些瓶颈之一。

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

在开始写之前不要尝试过度优化。继续编写易于维护、易读和尽可能无bug的代码。一旦事情正常工作,您就可以开始分析以查看真正的瓶颈。

过早优化是万恶之源" - Donald Knuth(97%的情况下是正确的)。

除非您对应用程序进行了概要分析,并且发现类封装是一个瓶颈,它在很大程度上降低了应用程序的速度,否则不要犹豫使用高级结构。它将为您带来许多好处,如可读性、可维护性和理解您所做的工作。这就是OOP:大规模程序的由来。

已经发布了一些好的答案,正如其他人所说,过早优化确实是不可取的。但是,让我从另一个角度来考虑你的问题。

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

从理论上讲,大多数正确编写的c++代码在完全开发的类中运行起来应该和没有开发的类一样快,但是

  1. 规则有例外;
  2. 从理论上讲,编写c++代码所需的努力可能太大了;和
  3. c++编译器的一些特性使编写错误的代码变得困难,但同时也使编写效率极低的代码变得非常容易。

逐条注释如下。

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

  2. 有时候,你知道你的数据在哪里,你想用它做什么,而c++精心设计的组织和访问控制设施只会成为你的障碍。在这种情况下,您可能会跳过高级c++,而只使用原始的、适合编程的、粗野的、挥舞着砍刀的C风格代码来做您想做的事情。实际上,c++显式地支持这种编码风格,使它可以——不,很容易——安全地将原始的C代码包装在一个模块中,从而在美丽的c++程序的其余部分隐藏它的恐怖。当然,如果你给你的代码一把砍刀,那么你就在冒险,不是吗,因为你的代码可能会破坏你不想要的数据,而你的编译器会站在一边,让它去做;但有时风险是值得的,有时风险甚至是程序员改变节奏的乐趣(和角色塑造)。

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

总而言之,我想说c++的取舍在80%的情况下是值得的。c++的组织和访问控制设施值得我们努力去正确地应用它们。如果您的问题是关于20%,那么,在我看来,有不止一种有效的编程方法。正如你所说,有时候"避免这些结构,按照基本的c++语法编写简单的代码"确实很有帮助。

通常没有。有时,是的。我认为前面的答案是正确的,但是,您所提出的特定示例可能更好地用枯燥、整洁、有序、没有技巧的c++来处理。

两件事:

  • 不要做任何过早的优化,现在的cpu很快,编译器很聪明,能够找出你在几个月的时间里看不到的优化。
  • 可以通过分析代码和/或使用条件编译来轻松检查内存泄漏之类的事情。泄漏不应该发生在发布版本上,所以你应该跳过检查。