Есть ли риски для оптимизации кода в C #? - PullRequest
20 голосов
/ 12 декабря 2011

На панели настроек сборки VS2010 Pro есть CheckBox с надписью «оптимизировать код» ... конечно, я хочу это проверить ... но, проявив необычайную осторожность, я спросил об этом моего брата, и он сказал, что он не проверен для отладки и что в C ++ он потенциально может делать вещи, которые могут привести к поломке или ошибкам кода ... но он не знает о C #.

Итак, мой вопрос, могу ли я установить этот флажок для моей сборки выпуска, не беспокоясь о том, что она нарушит мой код? Во-вторых, если он может сломать код, когда и почему? Ссылки на объяснения приветствуются.

The CheckBox I am talking about.

Ответы [ 8 ]

25 голосов
/ 12 декабря 2011

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

16 голосов
/ 12 декабря 2011

Оптимизация не должна нарушать ваш код. Здесь есть сообщение Эрика Липперта, которое объясняет, что происходит, когда вы включаете этот флаг. Прирост производительности будет варьироваться от приложения к приложению, поэтому вам нужно протестировать его с вашим проектом, чтобы увидеть, есть ли какие-либо заметные различия (с точки зрения производительности).

7 голосов
/ 12 декабря 2011

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

flag = false;

Thread t = new Thread(
   o =>
   {
        while(!flag)
        {
           // do stuff
        }
   });
t.Start();

// main thread does some work

flag = true;
t.Join(); // will never return in release mode if flag is not volatile

Это происходит из-за оптимизации компилятора, поскольку переменная flag кэшируется ядром потока t и, следовательно, не может видеть обновленное значение flag.

3 голосов
/ 12 декабря 2011

Должны ли оптимизации вносить ошибки? Нет.

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

Могут ли optimsations обнаруживать ошибки, которые всегда были в вашем коде, но скрыты, когда они выключены? Безусловно, случается совсем немного.

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

1 голос
/ 15 июня 2013

Например, у меня есть фрагмент кода из некоторых частей симуляции моей магистерской диссертации. В котором с включенным флагом оптимизации код на самом деле не ломает программу, но pathfinder выполняет только один запуск и циклы. (рекурсивный код замыкается в цикле на указателе пути, из которого он всегда прерывается при выключенном флаге оптимизации).

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

1 голос
/ 12 декабря 2011

В C # оптимизация НИКОГДА не должна нарушать ваш код.

Вместо этого при включенной оптимизации компилятор создает более компактный CIL при переводе между C # и CIL.

Я заметил (и, честно говоря, это интересно!), Что компиляторы C # из .NET <2.0 (1.0 и 1.1) производили такие же хорошие CIL БЕЗ оптимизаций, как более поздние компиляторы C # (2.0 и более поздние), производящие оптимизации WITH. </p>

0 голосов
/ 08 августа 2018

В моем случае, когда у меня был включен флаг оптимизации, он не завершил бы все операции, поэтому в конечном результате отсутствовали точки измерения, поэтому я просто отключил флаг оптимизации, чтобы исправить ошибку:

using System.Threading.Tasks;

                Parallel.Invoke(
                    async () => await ProcessPartialArrayOperationAssets(operationAssets, 0, operationAssets.Count / 2,
                        operations, inspection1),
                    async () => await ProcessPartialArrayOperationAssets(operationAssets, operationAssets.Count / 2,
                        operationAssets.Count, operations, inspection1)
                );

private async Task ProcessPartialArrayInspectionOperations(IList<InspectionOperation> operations,
    int begin,
    int end,
    Inspection inspection,
    InspectionAsset inspectionAsset)
{
    await Task.Run(() =>
    {
        // create one new operation measuring point for each measuring point in the operation's equipment
        int itemCounter = begin + 1;

        for (int i = begin; i < end; i++)
        {
            lock (_thisLock)
            {
                InspectionOperation operation = operations[i];
                int itemNumber = 1;

                // get the asset
                InspectionAsset operationAsset = operation.OperationAsset;
                if (operationAsset != null)
                {
                    // get the measuring points
                    string ABAPTrue = Abap.ABAP_TRUE;

                    lock (_thisLock)
                    {
                        IList<MeasuringPoint> measuringPoints = DbContext.MeasuringPoints.Where(x =>
                                x.AssetID == operationAsset.AssetID && x.InactiveFlag != ABAPTrue)
                            .ToList();

                        if (measuringPoints != null)
                        {
                            //Debug.WriteLine("measuringPoints.Count = " + measuringPoints.Count);

                            // create the operation measuring points
                            foreach (MeasuringPoint measuringPoint in measuringPoints)
                            {
                                OperationMeasuringPoint operationMeasuringPoint =
                                    new OperationMeasuringPoint
                                    {
                                        InspectionID = inspection.InspectionID,
                                        OperationNumber = operation.OperationNumber,
                                        SubActivity = "",
                                        RoutingNo = "",
                                        ItemNumber = itemNumber.ToString("D4"),
                                        // e.g. "0001", "0002" and so on
                                        ItemCounter = itemCounter.ToString("D8"),
                                        // e.g. "00000001", "00000002" and so on
                                        MeasuringPointID = measuringPoint.MeasuringPointID,
                                        MeasuringPointDescription = measuringPoint.Description,
                                        Equipment = inspectionAsset.AssetID,
                                        Category = "P"
                                    };
                                DbContext.Entry(operationMeasuringPoint).State = EntityState.Added;
                                itemNumber++;
                                itemCounter++;
                            }
                        }
                    }
                }
            }
        }
    });
}

Таким образом, я заменил вызов Parallel.Invoke на этот.К вашему сведению, эта проблема возникла при использовании .NET Framework 4.7.

await ProcessPartialArrayOperationAssets(operationAssets, 0, operationAssets.Count, operations, inspection1);

ОБНОВЛЕНИЕ:

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

    private void ProcessPartialArrayInspectionOperations(IList<InspectionOperation> operations,
        int begin,
        int end,
        Inspection inspection,
        InspectionAsset inspectionAsset)
    {
        // create one new operation measuring point for each measuring point in the operation's equipment
        int itemCounter = begin + 1;

        for (int i = begin; i < end; i++)
        {

            InspectionOperation operation = operations[i];
            int itemNumber = 1;

            // get the asset
            InspectionAsset operationAsset = operation.OperationAsset;
            if (operationAsset != null)
            {
                // get the measuring points
                string ABAPTrue = Abap.ABAP_TRUE;

                lock (_thisLock)
                {
                    IList<MeasuringPoint> measuringPoints = DbContext.MeasuringPoints.Where(x =>
                            x.AssetID == operationAsset.AssetID && x.InactiveFlag != ABAPTrue)
                        .ToList();

                    if (measuringPoints != null)
                    {
                        //Debug.WriteLine("measuringPoints.Count = " + measuringPoints.Count);

                        // create the operation measuring points
                        foreach (MeasuringPoint measuringPoint in measuringPoints)
                        {
                            OperationMeasuringPoint operationMeasuringPoint =
                                new OperationMeasuringPoint
                                {
                                    InspectionID = inspection.InspectionID,
                                    OperationNumber = operation.OperationNumber,
                                    SubActivity = "",
                                    RoutingNo = "",
                                    ItemNumber = itemNumber.ToString("D4"),
                                    // e.g. "0001", "0002" and so on
                                    ItemCounter = itemCounter.ToString("D8"),
                                    // e.g. "00000001", "00000002" and so on
                                    MeasuringPointID = measuringPoint.MeasuringPointID,
                                    MeasuringPointDescription = measuringPoint.Description,
                                    Equipment = inspectionAsset.AssetID,
                                    Category = "P"
                                };
                            DbContext.Entry(operationMeasuringPoint).State = EntityState.Added;
                            itemNumber++;
                            itemCounter++;
                        }
                    }
                }
            }
        }
    }

                        Parallel.Invoke(
                            () => ProcessPartialArrayInspectionOperations(operations, 0, operations.Count / 2,
                                inspection1, inspectionAsset),
                            () => ProcessPartialArrayInspectionOperations(operations, operations.Count / 2,
                                operations.Count, inspection1, inspectionAsset)
                        );

В качестве альтернативы, я думаю, я мог бы использовать Task.Run для каждого, а затем ждать Task.WhenAll(t1, t2, t3);, как объяснено здесь,но в этом случае я не делаю явных обращений к базе данных, поэтому я не думаю, что это применимо для использования Task.Run вместо Parallel.Invoke, хотя эта страница объясняет, почему мой Parallel.Invoke не завершал: Parallel.Invoke делаетне ждите завершения асинхронных методов

Подробнее см. в разделе «Параллелизм в C #» https://stephencleary.com/book/

0 голосов
/ 21 октября 2015

.net оптимизация компилятора может привести к ошибкам. случилось со мной сегодня. Мне понадобилось несколько часов, чтобы придать ему код:

for (int i = 0; i < list.Count-1; i++) {
  list[i+1].DoSomeThing();
  //some code
  if (someCondition) {
    list.insert(i+1, new Item());
    i++;
  }
}

в какой-то момент list[i+1] адресуется как list[i], как будто оба они указывают на один и тот же элемент. эта ошибка была такой странной. код хорошо работал в режиме отладки и в режиме выпуска, но когда я запустил его на стороне Visual Studio, напр. из файла .exe код потерпел крах. исправлено только отключение оптимизации компилятора.

...