将变量赋值给表达式是否更节省内存?

时间:2011-02-05 19:44:46

标签: c# .net

什么更有效?

decimal value1, value2, formula

此:

for(int i = 0; i>1000000000000; i++);
 {
        value1 = getVal1fromSomeWhere();
        value2 = getVal2fromSomeWhere(); 
        SendResultToA( value1*value2 + value1/value2);
        SendResultToB( value1*value2 + value1/value2);
 }

或者这个:

for(int i = 0; i>1000000000000; i++)
{
        value1 = getVal1fromSomeWhere();
        value2 = getVal2fromSomeWhere();
        formula = value1*value2 + value1/value2;
        SendResultToA(formula);
        SendResultToA(formula);
}

直觉我会选择后者......

我想在每次迭代(decimalformula)之间进行额外分配和在没有额外变量的情况下执行计算之间存在权衡...

编辑:
唔。上帝......每次提问时我都要经历这个问题吗? 如果我问它,那是因为 对事情给我,伙伴们。 每个人都不会生活在一个温和的非记忆批评世界中,醒来! 这只是一个非常简单的例子。我正在做百万的科学计算和多线程的东西,不要把我当作菜鸟:-)

所以绝对每纳秒都很重要。

PS:我几乎后悔C ++和指针。自动内存管理和GC绝对让开发人员无知和懒惰:-P

4 个答案:

答案 0 :(得分:7)

首先是配置文件,只有在必要时才进行此类微优化。否则优化可读性。在你的情况下,我认为第二个更容易阅读。

并且你的第二个代码有额外作业的陈述无论如何都不是真的。公式的结果需要存储在两个代码的寄存器中 编译代码后,额外变量的概念无效。例如,在您的情况下,编译器可以将formula存储在之前存储value1value2的寄存器中,因为它们的生命周期不会重叠。

如果第一个被优化到第二个,我不会感到惊讶。我认为这种优化称为“Common subexpression folding”。但当然,只有表达没有副作用才有可能。

检查IL并不总是足以看到什么得到优化。抖动也会优化。我有一些代码在IL中非常难看和缓慢,但在最终生成的x86代码中非常短。在检查机器代码时,您需要确保它实际上已经过优化。例如,如果您在VS中运行,即使发布代码未完全优化。

所以我的猜测是,如果编译器可以优化它们,它们同样快,而第二个更快,因为它不需要两次评估你的公式。

答案 1 :(得分:5)

除非你每秒这样做数万次,否则根本不重要。优化可读性和可维护性!

编辑:仇恨会讨厌,好吧,你走了。我的代码:

static void MethodA()
{
    for (int i = 0; i < 1000; i++) {
        var value1 = getVal1fromSomeWhere();
        var value2 = getVal2fromSomeWhere();
        SendResultToA(value1 * value2 + value1 / value2);
        SendResultToB(value1 * value2 + value1 / value2);
    }
}

static void MethodB()
{
    for (int i = 0; i < 1000; i++) {
        var value1 = getVal1fromSomeWhere();
        var value2 = getVal2fromSomeWhere();
        var formula = value1 * value2 + value1 / value2;
        SendResultToA(formula);
        SendResultToB(formula);
    }
}

由两者生成的实际x86程序集:

方法A:http://pastie.org/1532794

方法B:http://pastie.org/1532792

这些很长,因为它将来自Somewhere和SendResultTo [A / B]的getVal [1/2]内联,我连接到Random和Console.WriteLine。我们可以看到,确实,CLR和Jitter都不够聪明,不能复制先前的计算,所以我们花费额外的318字节的x86字节码来进行额外的数学运算。

但是,请记住这一点 - 即使是一个额外的页面错误或磁盘读/写,这些优化所带来的任何好处也会立即变得无关紧要。目前,CPU很少成为大多数应用程序的瓶颈--I / O和内存。优化空间局部性(即使用连续数组以减少页面错误),减少磁盘I / O和硬页面错误(即加载不需要的代码需要操作系统对其进行故障)。

答案 2 :(得分:3)

如果它可能很重要,我认为你是对的。两者都具有同等的可读性(可以说是)。

请记住,循环迭代次数与本地内存要求无关。你只是谈论几个额外的字节,(并且值将被放在堆栈上以便传递给函数,无论如何);而通过缓存计算结果来节省*的周期 会随着迭代次数而显着下降。

* 也就是说,只要编译器不为您执行此操作。查看每种情况下生成的IL是有益的。

答案 3 :(得分:1)

你必须反汇编字节码和/或基准测试,但我认为这可能是相同的,因为编译器看到formula(在循环范围内)是微不足道的不会改变,很容易直接'内联'(替换)。

编辑:正如用户CodeInChaos正确评论反汇编字节码可能还不够,因为只有在jitting之后才能进行优化。