首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如果要重复引用C数组值,是否应该将它们存储在局部变量中?

如果要重复引用C数组值,是否应该将它们存储在局部变量中?
EN

Stack Overflow用户
提问于 2009-03-19 15:50:00
回答 9查看 1.3K关注 0票数 3

我正在编写一些优化的C代码,它基本上贯穿于一个数组,并对每个元素做一些事情。它所做的事情取决于元素的当前值,因此类似于:

代码语言:javascript
复制
for (i=0; i < a_len; i++) {
    if (a[i] == 0) {
        a[i] = f1(a[i]);
    } else if (a[i] % 2 == 0) {
        a[i] = f2(a[i]);
    } else {
        a[i] = 0;
}

在使用动态语言工作多年之后,我将返回C语言,在那里,我的实践是尝试编写简单的代码,而不是为我可以直接引用的东西创建大量的局部变量,比如上面的ai。我非常清楚,最佳实践是编写可读的代码,并相信编译器比您更聪明,并且会进行很好的优化。

如果我在汇编程序中编写上面的代码,我会将ai加载到寄存器中一次,然后每次只使用这个值,因为我知道a[]是私有内存,不会在引用之间更改。但是,即使是智能编译器也可能每次都加载,因为它不能确定内存没有改变。(或者我是否必须显式声明"a“易失性,以使编译器不进行优化?)

因此,我的问题是:是否应该通过使用这样的局部变量重写来获得更好的性能:

代码语言:javascript
复制
for (i=0; i < a_len; i++) {
    val = a[i];
    if (val == 0) {
        a[i] = f1(val);
    } else if (val % 2 == 0) {
        a[i] = f2(val);
    } else {
        a[i] = 0;
}

或者像-O3这样的东西会自动帮我处理这个问题吗?我正在优化的代码需要几天的时间才能运行,所以即使是轻微的改进也会产生影响。

EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2009-03-19 16:16:31

f1f2的函数似乎具有相同的签名。他们的行为有多不同?你真的需要外面的支票吗?或者,你能把逻辑嵌入到一个函数中吗?

如果您有一个if-else梯形,而不是只有两个这样的函数,请尝试使用一个函数指针数组。使用a[ i ]的值对该数组进行索引并调用正确的函数。

手工优化往往是容易出错的微观优化。最好把这个任务留给编译器。如果你真的需要优化,看大图,考虑算法,设计,层等。

至于您的问题:是的,如果a[ i ]不声明为volatile,大多数编译器可能会优化内存读取。

票数 3
EN

Stack Overflow用户

发布于 2009-03-19 15:52:25

当然,最明显的答案是,首先以最易读/最简单/最易懂的方式编写它,然后以尽可能多的优化方式编译它,然后再对其进行基准测试和概要分析。

如果在你还不知道之前是瓶颈,那么优化它们是没有意义的。如果编译器自动完成该转换,那么您只会使代码变得更糟,花费时间,却得不到任何回报。也许除了冷静的感觉,但随着时间的推移,这种感觉会逐渐消失。:)

票数 14
EN

Stack Overflow用户

发布于 2009-03-19 16:06:58

为了可读性,先写出来。就我个人而言,我发现所有的订阅都会伤害我的眼睛,所以我可能会写得更像:

代码语言:javascript
复制
for (i=0; i < a_len; i++) {

    int val = a[i];  /* or whatever type */
    int result = 0;  /* default result */

    if (val == 0) {
        result = f1(val);
    } else if (val % 2 == 0) {
        result = f2(val);
    } 

    a[i] = result;
}

我猜编译器将生成类似的代码,并进行优化。但是,如果其中一个稍微好一点(只是稍微好一点),我就不会感到震惊。我敢打赌,如果是的话,那就是那个利用当地人的人。

另外,通过将使用索引遍历数组更改为使用指针遍历数组,您可能会得到很小的改进。同样,这非常依赖于编译器和情况。

代码语言:javascript
复制
for (p=&a[0]; p < &a[a_len]; ++p) {

    int val = *p;    /* or whatever type */
    int result = 0;  /* default result */

    if (val == 0) {
        result = f1(val);
    } else if (val % 2 == 0) {
        result = f2(val);
    } 

    *p = result;
}

而且,是的,我知道这些都是微优化,一般不应该担心(请先为可读性和正确性编写代码)--我只是指出了一些可能需要进行微优化的选项(这些建议必须支持对特定情况的分析)。

至于编译器是否会重复地从ai之类的地方重新加载,这取决于控制流,以及被访问的对象是全局的,还是已将其地址获取并传递给其他对象。

如果对象是全局的,或者已经获取了它的地址,并且您调用了一个函数,那么通常编译器必须假设该对象可能已经被函数修改,并且必须重新加载它。当使用指针将信息传递给函数时,也会发生类似的问题。使用局部变量可以帮助缓解这个问题,因为编译器可以非常容易地确定一个本地函数不会被一个被调用的函数修改,除非本地的地址被取下来。编译器也可以尝试使用某种全局优化(例如MSVC在链接时所做的)来解决这个问题。

即使数组a是全局的,示例代码也可能没有真正遇到这个问题,因为在调用了这些函数中的任何一个之后,您不会重新读取数组中的值(只对其进行写入)。

我想知道为什么markdown要从代码格式化块中删除空行?

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/662748

复制
相关文章

相似问题

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