С++: установить логическое значение, только если оно не установлено

У меня есть код в моем приложении C++, который обычно делает это:

bool myFlag = false;
while (/*some finite condition unrelated to myFlag*/) {
    if (...) {
        // statements, unrelated to myFlag
    } else {
        // set myFlag to true, perhaps only if it was false before?
    }
}
if (myFlag) {
    // Do something...
}

Вопрос, который у меня есть, относится к оператору else моего кода. По сути, мой цикл может установить значение myFlag с ложного на истинное, основываясь на невыполнении определенного условия. Никогда флаг не будет сброшен с true на false. Я хотел бы знать, какое утверждение имеет больше смысла с точки зрения производительности и, возможно, действительно ли эта проблема не является проблемой из-за оптимизации компилятора.

myFlag = true;

OR

if (!myFlag) myFlag = true;

Обычно я выбираю первое, потому что оно требует написания меньшего количества кода. Однако я начал задаваться вопросом, что, возможно, это связано с ненужной записью в память, и, следовательно, последняя предотвратит ненужную запись, если myFlag уже имеет значение true. Но займет ли использование последнего больше времени, потому что есть условный оператор и, следовательно, компиляция кода с использованием большего количества инструкций?

Или, может быть, я слишком много думаю об этом...


ОБНОВЛЕНИЕ 1

Просто немного поясню... цель моего последнего случая - не записывать в память, если переменная уже была истинной. Таким образом, записывайте в память только в том случае, если переменная имеет значение false.


person ecbrodie    schedule 22.02.2013    source источник
comment
Вы слишком много думаете об этом.   -  person Oliver Charlesworth    schedule 23.02.2013
comment
Как правило, myFlag = true будет быстрее, однако если вы хотите узнать, что быстрее у вас имеется для профилирования в вашей собственной системе.   -  person Jack Aidley    schedule 23.02.2013
comment
Вы просто замените ненужную запись на ненужное чтение и переход и угадаете, что медленнее.   -  person GManNickG    schedule 23.02.2013
comment
Единственный раз, когда вам нужно беспокоиться о таких вещах, это когда вы пишете в переменные, которые используются совместно с другими потоками. Если бы myFlag был общим (это не в вашем случае), то запись в него без разбора сделала бы строку кэша dirty, которая отследила бы его из всех других ядер, которые могли иметь копию clean, и вы должны учитывать false sharing - но то я передумываю ответ :-)   -  person amdn    schedule 28.02.2013


Ответы (6)


Вам почти наверняка лучше просто использовать myFlag = true;.

Лучшее, на что вы можете надеяться от if (!myFlag) myFlag = true;, это то, что компилятор заметит, что if не имеет значения, и оптимизирует его. В частности, оператор if должен читать текущее значение myFlag. Если значение еще не находится в кеше, это означает, что инструкция остановится в ожидании чтения данных из памяти.

Напротив, если вы просто записываете (без предварительного тестирования), значение может быть записано в очередь записи, а затем сразу же может выполняться больше инструкций. Вы не получите задержку, пока/если вы не прочитаете значение myFlag (и если предположить, что оно будет прочитано достаточно скоро после записи, оно, вероятно, все еще будет в кеше, поэтому задержка будет минимальной).

person Jerry Coffin    schedule 22.02.2013
comment
ЦП может останавливаться при записи, но не при чтении. Просто маловероятно. - person Jack Aidley; 23.02.2013
comment
@JackAidley: О, да, по крайней мере теоретически (например, если у вас есть только кэширование со сквозной записью). Обстоятельства, приводящие к замедлению записи, более чем маловероятны, хотя и находятся где-то на грани между крайне редкими и чисто теоретическими. - person Jerry Coffin; 23.02.2013

Вы же понимаете, что чек спорный, верно? Если вы вслепую установили его на true, а он не был установлен, вы устанавливаете его. Если он уже был true, то изменений нет, и вы не устанавливаете его, поэтому вы можете эффективно реализовать его как:

myFlag = true;

Что касается потенциальных оптимизаций, то для возможности тестирования значение должно находиться в кэше, поэтому большая часть затрат уже оплачена. С другой стороны, ветвь (если компилятор не оптимизирует if, что в большинстве случаев будет) может оказать большее влияние на производительность.

person David Rodríguez - dribeas    schedule 22.02.2013

С точки зрения циклов ЦП, предпочтительнее myFlag = true; Подумайте об этом: даже если компилятор не выполняет оптимизацию (что маловероятно), просто его установка занимает один оператор asm, а прохождение if занимает как минимум 1 оператор asm.

Так что просто выполняйте задание.

И что еще более важно, не пытайтесь строить гипотезы на таких низкоуровневых деталях, конкретные оптимизации компилятора могут полностью противоречить интуиции.

person Qortex    schedule 22.02.2013
comment
Если логическое значение находится в памяти, узким местом, скорее всего, будет чтение/запись памяти, а не ЦП. - person Jack Aidley; 23.02.2013
comment
хорошо, но в любом случае вам придется получить его из памяти, чтобы оценить if, и тогда он, скорее всего, останется в кэш-памяти. Таким образом, доступ к памяти необходим в обоих случаях, если только она еще не находится в кеше. - person Qortex; 23.02.2013

Вы, скорее всего, слишком много думаете о проблеме, как уже упоминали другие, поэтому позвольте мне сделать то же самое. Следующее может быть быстрее, если вы можете позволить себе дублировать операторы, не связанные с myFlag. На самом деле, вы можете избавиться от myFlag. Хорошо, поехали:

while (/*some finite condition*/) {
    if (...) {
        // statements
    } else {
        while (/*some finite condition*/) {
            if (...) {
               // statements, repeated
            }
        }
        // Do something (as if myFlag was true in the OPs example)
        break;
    }
}

Как и при любой оптимизации производительности: измеряйте, измеряйте, измеряйте!

person Daniel Frey    schedule 22.02.2013

Это зависит от архитектуры, будет ли if (!myFlag) myFlag = true; выполняться дольше, чем простое myFlag = true;, даже без какой-либо оптимизации. Существуют архитектуры (например, https://developer.qualcomm.com/hexagon-processor), в которых оба оператора будут выполняться только по одному циклу.

Единственный способ выяснить это на вашей машине — это измерить.

В любом случае myFlag = true всегда будет быстрее или будет иметь такое же время выполнения, как if (!myFlag) myFlag = true;

person A. K.    schedule 28.02.2013

Этот вопрос тоже вызвал у меня головную боль, поэтому я просто проверил его сам, используя следующий код (C#):

        System.Diagnostics.Stopwatch time = new System.Diagnostics.Stopwatch();
        int i = 0;
        int j = 1;

        time.Start();
        if (i != 0)
            i = 0;
        time.Stop();
        Console.WriteLine("compare + set - {0} ticks", time.ElapsedTicks);

        time.Reset();
        time.Start();
        if (j != 0)
            j = 0;
        time.Stop();
        Console.WriteLine("compare - {0} ticks", time.ElapsedTicks);


        time.Reset();
        time.Start();
        i = 0;
        time.Stop();
        Console.WriteLine("set - {0} ticks", time.ElapsedTicks);

        Console.ReadLine();

результат:

сравнить + установить - 1 тик

сравнить - 1 тик

установить - 0 тиков

в то время как время, используемое для установки значения, конечно, не равно нулю, это показывает, что даже один запрос требует больше времени, чем просто установка переменной.

person oachkatzl    schedule 21.11.2013