【问题标题】:What is the standard approach for implementing a one-time Boolean switch?实现一次性布尔开关的标准方法是什么?
【发布时间】:2009-09-21 16:24:05
【问题描述】:

假设我有如下代码,processData 每分钟执行数百甚至数千次:

class DataProcessor {
private:
    DataValidator* validator;
    bool atLeastOneDataPoint;
    bool dataIsValid(Data* dataToValidate) {
        return validator->validate(dataToValidate);
    }

public:
    // ...

    void processData(Data* dataToProcess) {
        if (dataIsValid(dataToProcess) || !atLeastOneDataPoint) {
            // process data
            // ...
            atLeastOneDataPoint = true;
        }
    }

    // ...
}

从它的名字可以推断,atLeastOneDataPoint 是一个真正只需要设置一次的变量,但在上面的代码中,每次在第一个数据点之后调用processData 时都会设置它。当然,我可以将赋值行更改为:

if (!atLeastOneDataPoint) atLeastOneDataPoint = true;

但这只会用一堆不必要的布尔检查替换一堆不必要的分配。

我不关心这段代码的性能;真的,我只是被做一些完全不必要的事情的想法所困扰。有没有一种标准的方式来设置这样的一次性开关,在设计上更直观“合适”?

至于关心这个是否会让我成为一个糟糕的程序员:请让我们改天再讨论吧。

【问题讨论】:

    标签: language-agnostic performance boolean switch-statement


    【解决方案1】:

    我想说你已经得到的是最好的方法:只需在每次迭代时设置布尔标志。

    如果您不关心性能,我认为这是最易读的解决方案:这很好而且很明显 - 我已经处理了一个有效的数据点,所以我将标志设置为 true。

    即使性能是一个问题,我认为您也很难找到任何比将单个布尔值设置为 true 更有效的操作。

    【讨论】:

      【解决方案2】:

      回调

      先注册一个名为“call_once_at_init()”的函数,该函数会注册“call_always_after_init()”以供以后使用。

      【讨论】:

      • @Tiemen:我想过这个问题,我想我唯一担心的是使用回调函数实际上可能比反复分配效率低....你碰巧知道吗? (当然,这绝对是我所要求的更“直观地正确”;我只是好奇。我想我想同时拥有它,也许这是不可能的。)
      • 如果您只打算“每分钟数百或数千次”执行它,那么您可以做的任何事情都不会影响性能。当代码执行数千万次每秒时开始担心性能。
      • 假设性能很重要,但我认为 Dan 的直觉是正确的,即函数指针可能效率较低。加载地址并调用它可能比调用常量地址要慢。或者可能是相同的速度(因为无论如何都可能从常量池中加载常量,在这种情况下,它取决于当前缓存中的内容)。或者可能无关紧要,因为调用代码允许编译器仅加载函数地址一次并将其保存在被调用代码从未实际使用的寄存器被调用者保留寄存器中。或者可能更快。
      • @jalf:是的,我知道。就像我说的那样,我真的只是想知道我是否缺少一些明显的替代方案。我认为事实是这个答案在概念上可能是最好的,而 teedyay 的答案(我已经在使用的方法)最实际,我只需要接受。
      • @onebyone:感谢您的洞察力。这也是我所怀疑的;正如 jalf 和 teedyay 已经指出的那样,这可能并不重要,真的。
      【解决方案3】:

      如果您不使用 atLeastOneDataPoint 作为流程数据中的第一个标志

      void processData(Data* dataToProcess) {
         if ( (atLeastOneDataPoint = (dataIsValid(dataToProcess) || !atLeastOneDataPoint) ) ) {
            // process data
            //...
         }
      }
      

      但是每分钟 10000 次不算什么 - 当它每秒 150 万次时担心

      【讨论】:

        【解决方案4】:

        这不是你想要的吗?

        void processData(Data* dataToProcess) {
            if (!atLeastOneDataPoint) atLeastOnedataPoint = true;
            if (dataIsValid(dataToProcess)) {
                // process data
                // ...
            }
        }
        

        这实际上只执行atLeastOnedataPoint = true 一次。

        即使每次都这样做,if (!atLeastOneDataPoint) atLeastOnedataPoint = true; 也应该花费不到 100 ns(可能要少得多)。即使在 100 ns 时,如果您在一分钟内执行 1000 次,那一分钟也将消耗 100us = 0.1ms。

        我打赌你有“更大的鱼要炸”。

        【讨论】:

        • 难道分支不会比重复分配更糟糕吗? (不是说不用测量等就可以分辨出来)
        • @Mike:你说比较比赋值更快是对的。 (是吗?我真的不好意思说我不知道​​。)但是,你改变了代码的逻辑;只要 dataIsValid 返回 true 或者还没有数据点,就需要处理数据。通过在检查 dataIsValid 之前设置它,您实际上是在剥夺 atLeastOneDataPoint 的目的。
        • @Dan:这就是我将其作为问题发布的原因。可能测试和赋值语句应该在下面的 if 语句中,尽管这并不重要。
        • @mmyers:你说得对,不过,如果任务真的更复杂,你可能想检查一下。原谅我,我认为这些精明的问题(包括我自己)引发了多少讨论。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-17
        • 1970-01-01
        • 2021-12-25
        • 2011-04-26
        • 2018-12-30
        相关资源
        最近更新 更多