首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C和C++中的效率

C和C++中的效率
EN

Stack Overflow用户
提问于 2014-03-10 14:35:53
回答 4查看 930关注 0票数 1

所以我的老师告诉我,我应该根据需要实时计算中间结果,而不是存储它们,因为现在处理器的速度要比内存的速度快得多。

因此,当我们计算一个中间结果时,我们也需要使用一些内存,对吗?有人能给我解释一下吗?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-03-10 14:53:12

因此,当我们计算一个中间结果时,我们也需要使用一些内存,对吗?有人能给我解释一下吗?

一台计算机有几级内存。这些图层看起来像这样

  1. 寄存器- CPU对此进行所有计算,并且访问是即时的。
  2. 高速缓存--与CPU核心紧密耦合的内存;主系统内存的所有内存访问实际上都要经过缓存,如果数据来自系统内存,则访问程序。如果缓存中存在数据,并且访问对齐良好,那么访问也几乎是即时的,因此访问速度非常快。
  3. 主系统内存-通过内存控制器连接到CPU,并由系统中的CPU核心共享。访问主存通过寻址和内存与CPU之间的有限带宽引入延迟。

当您使用就地计算的中间结果时,这些结果通常永远不会离开寄存器,或者可能仅限于缓存,因此不受可用系统内存带宽的限制,也不受内存总线仲裁或地址生成联锁的阻碍。

票数 2
EN

Stack Overflow用户

发布于 2014-03-10 14:54:03

你的老师说得对,现在处理器的速度要比内存的速度快得多,对内存的访问速度要慢得多:缓存、寄存器等。

假设您想要计算一个三角函数: sin(x)。要做到这一点,您可以调用函数(数学库提供一个函数,也可以实现自己的函数),即计算值;或者可以使用存储在内存中的查找表来获得结果,这意味着存储中间值(某种程度)。

调用一个函数将导致执行许多指令,而使用查找表则会减少指令(获取LUT地址、获取所需元素的偏移量、从address+offset读取)。In this case, storing the intermediate values is faster

但是如果要执行c = a+b,计算值要比从内存中的某个地方读取值要快得多。请注意,在这种情况下,要执行的指令数量将是相同的。

因此,虽然访问RAM的速度确实较慢,但是否值得访问RAM而不是进行计算是一个明智的问题,需要考虑以下几点:要执行的指令数,如果计算发生在一个循环中,并且可以利用架构、管道、缓存内存等。没有一个答案,您需要单独分析每一种情况。

票数 5
EN

Stack Overflow用户

发布于 2014-03-10 15:59:14

你的老师的建议在一个复杂的话题上过于简单化了。

如果你认为“中间”是一个术语(在单词的算术意义上),那么问问自己,你的代码是否在其他地方重复使用这个术语?也就是说,如果您的代码如下:

代码语言:javascript
复制
void calculate_sphere_parameters(double radius, double & area, double & volume)
{
    area = 4 * (4 * acos(1)) * radius * radius;
    volume = 4 * (4 * acos(1)) * radius * radius * radius / 3;
}

你是否应该这样写:

代码语言:javascript
复制
void calculate_sphere_parameters(double radius, double & area, double *volume)
{
    double quarter_pi = acos(1);
    double pi = 4 * quarter_pi;
    double four_pi = 4 * pi;
    double four_thirds_pi = four_pi / 3;
    double radius_squared = radius * radius;
    double radius_cubed = radius_squared * radius;

    area = four_pi * radius_squared;
    volume = four_thirds_pi * radius_cubed;    // maybe use "(area * radius) / 3" ?
}

现代优化编译器为这两者发出相同的二进制代码并不是不可能的。我把它留给读者来决定他们喜欢在源代码中看到什么.

许多简单的算法也是如此(至少,如果计算中不涉及函数调用的话)。此外,现代编译器和/或CPU指令集可能能够免费进行“偏移”计算,例如:

代码语言:javascript
复制
for (int i = 0; i < N; i++) {
    do_something_with(i, i + 25, i + 314159);
}

结果将与以下内容相同:

代码语言:javascript
复制
for (int i = 0; i < N; i++) {
    int j = i + 25;
    int k = i + 314159;
    do_something_with(i, j, k);
}

因此,主要的规则应该是,如果您的代码的可读性不能通过创建一个新变量来保存“临时”计算的结果,那么使用一个变量可能是过分的。

另一方面,如果您在十行代码中使用i + 12345十几次.命名它,并评论为什么这个奇怪的硬编码偏移是如此重要。

记住,仅仅因为源代码包含一个变量,并不意味着编译器发出的二进制代码将为该变量分配内存。编译器可能会得出这样的结论:这个值甚至没有被使用(并且完全放弃了分配它的计算),或者它可能得出结论,它“只是一个中间的”(以后再也不用它从内存中检索),所以将它存储在寄存器中,在“最后一次使用”之后覆盖它。与从内存位置检索值相比,每次需要时执行类似于计算值i + 1之类的操作要有效得多。

我的建议是:

  • 首先要保持代码的可读性--太多的变量比较模糊,而不是帮助。
  • 不要费心地保存“简单”的中间物--加/减或按二次方的乘积几乎是一种“免费”操作。
  • 如果在多个地方重用相同的值(“算术术语”),如果计算成本很高,就保存它(例如,涉及函数调用、很长的算术序列,或者像数组校验和这样的大量内存访问)。
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22303126

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档