【问题标题】:Performance implications of using a variable versus a magic number [closed]使用变量与幻数的性能影响[关闭]
【发布时间】:2013-10-18 18:58:10
【问题描述】:

我经常对此感到困惑。我一直被教导使用变量或常量来命名我经常使用的数字,但是如果它降低了程序的效率,我还应该这样做吗?举个例子:

private int CenterText(Font font, PrintPageEventArgs e, string text)
    {
        int recieptCenter = 125;
        int stringLength = Convert.ToInt32(e.Graphics.MeasureString(text, font));
        return recieptCenter - stringLength / 2;
    }

以上代码使用了命名变量,但运行速度比这段代码慢:

private int CenterText(Font font, PrintPageEventArgs e, string text)
    {
        return 125 - Convert.ToInt32(e.Graphics.MeasureString(text, font) / 2);
    }

在这个例子中,执行时间的差异很小,但是在更大的代码块中呢?

【问题讨论】:

  • 在“可读性”和“简洁性”之间存在主观灰色地带............意见会有所不同。
  • 我更喜欢第一个,因为从现在起 6 个月后,有人可能会弄清楚我做了什么。但我会为 125 和 2...... 定义“常量”值,所以这些值意味着什么......不仅仅是一些随机的 int 挂在那里。
  • 这样的性能差异是您最不必担心的。此外,我确信这是在调试器中测量的,这意味着您的基准测试不准确。此外,我确信 const 的执行效果与硬编码值相同。
  • 1) 即使存在差异,我发现它非常、非常、非常不可能在您的应用程序中成为一个真正的问题,从而导致一个太慢的差异和一个足够快的。 2)你是如何衡量差异的?奇怪的是,您遇到了许多常见的基准测试陷阱之一。对微优化进行基准测试非常很难。
  • 您知道这两种方法略有不同吗?第一个将测量值转换为整数,然后分成两半。第二种方法将测量值分成两半然后转换为整数。我想知道这是否会导致您的可测量差异(尽管我怀疑这是一个错字)。老实说,我很惊讶。对我来说,我希望运行时 jitted 代码(假设它在没有附加调试器的情况下以发布模式运行)几乎相同。在第一个示例中,我没有看到编译器无法优化以生成第二个示例。

标签: c# magic-numbers


【解决方案1】:

当他们说“使用常量”时,他们的字面意思是“使用常量”;它们并不意味着“使用永不改变的变量”。

这相当于你的代码:

const int recieptCenter = 125;
int stringLength = Convert.ToInt32(e.Graphics.MeasureString(text, font));
return recieptCenter - stringLength / 2;

使用const 关键字,编译器知道125 永远不会改变,并且能够应用它应用于以文字表示的常量的优化。

命名你的“神奇数字”有一个巨大的优势:在你离开公司后维护你的代码的人会知道125 的含义。当您在几年后重新访问此代码时,它也会对您有所帮助。

【讨论】:

  • It will help you, too, when you come back to revisit this code in a few years. - 甚至几个星期。
【解决方案2】:

使用变量和硬编码值之间的差异在最坏的情况下可以忽略不计。编译器可以很好地处理这样的事情。如果您看到性能差异,我想听听您收集这些指标的方法。 (您的测试本身可能是可疑的,并且很可能无法重复。)

在任何情况下,您都应该首先担心您的程序是否正确和可维护。这意味着:

  • 仔细命名您的类、方法和变量
  • 分离关注点
  • 避免使用幻数和字符串(125 到底是什么意思?它是什么意思?)
  • 避免编码

此外,receiptCenter 听起来也不应该是一个常数。它可能很少更改,但我建议您将其存储在应用程序之外的配置文件或数据库表等中。如果需要更改该值,则必须重新编译并将整个内容推送到 prod。另外,将您的软件发布到其他收据中心值不同的地方怎么样?您只想更改配置设置,而不是仅为该实例构建不同版本的应用程序。

优化是您最不担心的事情,除非这是您首先要担心的事情,而且这是架构师/专家级别的考虑。

【讨论】:

  • 我在问题中看不到任何过早的优化。其余的肯定是真的。 PS:hardoding 中的错字(缺少“c”)。
  • @JensG:感谢您指出错字。对于过早的选择。请仔细阅读 OP 的标题:Performance implications of using a variable versus a magic number 这就是过早优化的定义,而且是一个非常n00b 的定义。另请参阅我的编辑。
  • 好的,同意。现在我看到了:-)
  • @PaulSasik 公平地说,最初的问题标题不同(而且完全没有描述性),所以我对其进行了编辑。那是我写的标题,不是OP。
  • @Servy:很高兴知道,但 OP 确实在第一句话中回应了对性能的担忧:but if it reduces the efficiency of the program。因此,即使标题更改,我也认为是prem。同上。想法和批评成立。此外,这个看似简单的问题一定是软件设计问题的冰山一角……仅仅讨论一个名为receiptCenter 的常量的定义就给我带来了危险。是的,我假设基于名称的一组语义,如果变量实际上命名良好,那么它们是正确的,值应该来自配置文件。
【解决方案3】:

receiptCenter 应该是在该私有方法之外的某个地方声明的常量,在某个明显的地方,所有常量都一起声明。或者,它可以是从配置中读取的变量。

当您在类库深处的私有方法中的某处为常数编号命名时,它确实不会改进您的代码。

【讨论】:

  • 如果变量声明在一个地方,对理解变量的含义有何帮助?如果常量只用在一种方法中,为什么要把声明和用法分开呢?为什么要从配置中读取 pi 的值?常量确实改进了代码,即使它是在相同的方法中声明的,因为它增加了数字含义的清晰度。
  • 一个例子是配置。即使您只使用该常量一次,您仍然可能需要稍后更改它的选项。如果从配置文件或数据库中读取数字,肯定会让您的生活更轻松。
  • +1 考虑到这更好地从配置中读取。我怀疑这个变量是否符合真正的常量语义,如 Math.PI。另外,@MarkusMeyer:要查看一组常量,将您最喜欢的反编译器指向 .NET 中的 System.Math 类,以了解 Justandrei 在说什么。
猜你喜欢
  • 2018-06-28
  • 1970-01-01
  • 2014-03-12
  • 1970-01-01
  • 2023-01-16
  • 2023-04-01
  • 2011-04-05
  • 1970-01-01
  • 2017-02-01
相关资源
最近更新 更多