Действительно ли полезно в C# применить теорему DeMorgan для ручной оптимизации булевых выражений в условных операторах (например, если условия)

Предположим, у вас есть этот файл .config:

<configuration>
    <configSections>
        <section name="mySection" type="ConsoleApplication1.MySection, ConsoleApplication1" /> // update type  & assembly names accordingly
    </configSections>

    <mySection>
        <MyCollection default="one">
            <entry name="one" />
            <entry name="two" />
        </MyCollection>
    </mySection>
</configuration>

Затем с помощью этого кода:

public class MySection : ConfigurationSection
{
    [ConfigurationProperty("MyCollection", Options = ConfigurationPropertyOptions.IsRequired)]
    public MyCollection MyCollection
    {
        get
        {
            return (MyCollection)this["MyCollection"];
        }
    }
}

[ConfigurationCollection(typeof(EntryElement), AddItemName = "entry", CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class MyCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new EntryElement();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        if (element == null)
            throw new ArgumentNullException("element");

        return ((EntryElement)element).Name;
    }

    [ConfigurationProperty("default", IsRequired = false)]
    public string Default
    {
        get
        {
            return (string)base["default"];
        }
    }
}

public class EntryElement : ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
    public string Name
    {
        get
        {
            return (string)base["name"];
        }
    }
}

вы можете прочитать конфигурацию с атрибутом «default», например так: :

    MySection section = (MySection)ConfigurationManager.GetSection("mySection");
    Console.WriteLine(section.MyCollection.Default);

Это выведет «один»

10
задан Jon Erickson 11 June 2009 в 18:55
поделиться

15 ответов

На процессорах с такой скоростью практически невозможно изменить порядок логических выражений, чтобы реально изменить скорость. А компилятор C # очень умен, тоже оптимизирует. Оптимизируйте для удобства чтения и ясности!

31
ответ дан 3 December 2019 в 13:13
поделиться

Для всех нас, не связанных с CS:

википедия о законах Де Моргана :

Законы Де Моргана - это правила, касающиеся логические операторы "и" и "или" в терминах друг друга через отрицание, а именно:

НЕ (P ИЛИ Q) = (НЕ P) И (НЕ Q)
НЕ (P И Q) = (НЕ P) ИЛИ (НЕ Q)

0
ответ дан 3 December 2019 в 13:13
поделиться

Закон Де Моргана полезен для приведения его к нормальной форме, например, дизъюнктивной нормальной форме (DNF) или конъюнктивной нормальной форме (CNF). В основном это означает, что это либо

DNF: (a, b и c) ИЛИ (e, f и g) ...

или

CNF: (a, b или c) AND (e или f или g) ....

Вы можете добавить НЕ на самом низком уровне.

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

0
ответ дан 3 December 2019 в 13:13
поделиться

Сначала займитесь ремонтопригодностью и оптимизацией высокого уровня .

Затем займитесь оптимизацией низкого уровня.

0
ответ дан 3 December 2019 в 13:13
поделиться

Я согласен с общими утверждениями, что Читаемость и удобство обслуживания наиболее важны в наши дни, когда дело доходит до оптимизации логических выражений. Поэтому теорема ДеМоргана обычно очень полезна.

Есть одно исключение из этого правила. Если логическое выражение изменяет выражение, оптимизированное по теореме Деморгана, его будет труднее поддерживать. Рассмотрим выражение с несколькими входными данными, которое было оптимизировано для отображения только нескольких логических условий. Одно изменение требуемой логической логики может заставить кого-то снова перечислить все возможные булевы комбинации и затем произвести повторную оптимизацию. Если бы выражение было оставлено в неоптимизированном формате, для выполнения изменения потребовалось бы меньше шагов.

С точки зрения анекдота, я задаюсь вопросом, уменьшит ли обучение команду теореме ДеМоргана, картам Карно и т. Д. Ненужные / неэффективные булевы выражения. . Возможно, если кто-то хорошо разбирается в этих методах, он / она будет лучше выражать свои мысли. Например, я недавно наткнулся на это логическое выражение в коде поддерживаемого мною программного обеспечения:

if ({boolean variable} != true && false)
1
ответ дан 3 December 2019 в 13:13
поделиться

Оптимизатор C # не может сделать слишком много, учитывая правила короткого замыкания для вычисления логических выражений. Таким образом, применение закона ДеМоргана мало что даст, если только оно не позволит вам увидеть другие полезные рефакторинги (и, конечно, поможет сделать ваш код более понятным).

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

if ( costly_boolean_function() && cheap_often_false_boolean_function() )

. Оптимизаторы запросов SQL делают это само собой разумеющимся, поскольку SQL не имеет короткого замыкания. Оптимизатор запросов будет агрессивно переупорядочивать предикаты конъюнктивного предложения WHERE (в форме c1 AND c2 AND ... cn ), чтобы поставить на первое место наименее затратные условия, поскольку они могут давать ложные оценки и устранять необходимость оценивать более дорогие.

1
ответ дан 3 December 2019 в 13:13
поделиться

Поскольку при вычислении логических выражений используется сокращенная семантика, вы можете переместить подвыражения, которые дешевле вычислять, на передний план:

if (CountAllFilesOnDrive('C:\') > 7 && useFileScan) { ... }

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

if (useFileScan && CountAllFilesOnDrive('C:\') > 7) { ... }

DeMorgan может помочь вам переместить «ранние выходы» на передний план и, таким образом, повысить среднюю производительность.

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

3
ответ дан 3 December 2019 в 13:13
поделиться

Практически во всех практических случаях, которые я могу придумать, расположение логических операторов не оказывает заметного влияния на общую производительность. Если ваша программа ожидает базы данных, сети и т. Д., Она будет проводить там гораздо больше времени, чем в этих крошечных операциях. Если вы пишете программу, в которой это действительно важно, лучше пропустите C # и используйте вместо него C ++.

2
ответ дан 3 December 2019 в 13:13
поделиться

DeMorgan сам по себе может быть совершенно неуместным при наличии оценки короткого замыкания.

return !(exp1 || exp2);
return !exp1 && !exp2;

скомпилирован в

if(   exp1 ) return !(true); else return !(exp2);
if(!(!exp1)) return   false; else return !(exp2);

с отмененными not и сложенными константами, эти идентичны.

Более важный случай - это порядок оценки; поставьте перед выражениями дешевые вещи, которые могут вызвать короткое замыкание. Компилятор не может оптимизировать это для вас, потому что ему трудно обнаружить семантические проблемы, такие как побочные эффекты или если более позднее выражение делает предположения, основанные на более ранних:

return validState() && checkAssumuingValidState();
2
ответ дан 3 December 2019 в 13:13
поделиться

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

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

3
ответ дан 3 December 2019 в 13:13
поделиться

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

Иногда, когда логика сложна, мне нужно создать Карно Карно , чтобы упростить логику до того, что я могу даже записать. Часто использование K-Maps может помочь вам придумать более лаконичные способы выражения вашей логики. Результат может иметь или не иметь смысла, но он будет эквивалентен.

И я бы также сказал, что сам ДеМорган на самом деле не является оптимизацией, которая будет иметь значение, если более половины терминов отрицательны (НЕ) , вы в лучшем случае получите производительность, удалив несколько НЕ, что является одной инструкцией для ЦП на НЕ. В худшем случае, вы можете добавить столько НЕ, сколько уберете, и если вам не нужно использовать ДеМорган, вы получите больше НЕ, чем было изначально.

Если вы собираетесь оптимизировать логику, используйте некоторую логическую алгебру, или мой личный фаворит K-Maps , чтобы уменьшить количество терминов (если возможно). Не просто перемещайте логические операторы, это глупо.

3
ответ дан 3 December 2019 в 13:13
поделиться

Оптимизация в JIT в ее текущей форме (из того, что я читал) не оптимизирует это для вас. Если вам нужно его оптимизировать, вам все равно придется это учитывать.

При этом, это довольно небольшая микрооптимизация. В общем, я бы предпочел писать ваши «нетривиальные логические выражения» в более выразительной форме, чтобы их было легче понять. Для меня это более ценно, чем любая очень маленькая оптимизация, которую вы получите от применения теоремы де Моргана.

7
ответ дан 3 December 2019 в 13:13
поделиться

Я считаю, что правильный ответ на этот вопрос состоит в том, что компилятор не выполняет (обычно) оптимизировать логические вычисления просто из-за логического короткого замыкания, например:

if (GetFlagA() || GetFlagB())
{
   ...do something
}

Порядок выполнения, если оценка действительно имеет значение, если вызов GetFlagA изменяет то, на что полагается GetFlagB (при условии, что это ДЕЙСТВИТЕЛЬНО плохая практика кода, но это другая тема для другого потока). Проблема здесь в логическом коротком замыкании, если GetFlagA запускается и возвращает true, GetFlagB никогда не запускается, как видно здесь, результат GetFlagB несущественен для оценки оператора.

A | B | =

F | F | F

F | Т | Т

Т | F | T истинно независимо от возвращаемого значения B.

T | Т | T истинно независимо от возвращаемого значения B.

Таким образом, вопрос о том, можете ли вы оптимизировать с помощью Деморгана или чего-то еще, на самом деле похож на остальную часть информатики и разработки программного обеспечения. "Это зависит." если вы используете нефункциональную оценку, ее, вероятно, можно оптимизировать. Честно говоря,

7
ответ дан 3 December 2019 в 13:13
поделиться

Учитывайте удобочитаемость и обслуживание. Если у вас есть довольно сложный набор логических выражений, которые трудно читать, теория ДеМоргана может быть отличным подходом к сокращению выражения до чего-то более простого для чтения / поддержки, но все же действительного / совместимого с исходным выражением.

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

2
ответ дан 3 December 2019 в 13:13
поделиться

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

Теорема ДеМоргана может быть полезным инструментом для этого.

8
ответ дан 3 December 2019 в 13:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: