Are there risks to optimizing code in C#?

后端 未结 8 826
粉色の甜心
粉色の甜心 2020-12-18 18:53

In the build settings panel of VS2010 Pro, there is a CheckBox with the label \"optimize code\"... of course, I want to check it... but being unusually cautious, I asked my

相关标签:
8条回答
  • 2020-12-18 19:26

    The optimizations shouldn't really break your code. There's a post here by Eric Lippert which explains what happens when you turn that flag on. The performance gain will vary from application to application, so you'll need to test it with your project to see if there are any noticeable differences (in terms of performance).

    0 讨论(0)
  • 2020-12-18 19:28

    In C# the optimization should NEVER break your code.

    Instead, with optimizations turned on the compiler produces more compact CIL when translating between C# and CIL.

    I observed (and frankly it's interesting!) that the C# compilers from .NET < 2.0 (1.0 and 1.1) produced as good CIL WITHOUT optimizations as later C# compilers (2.0 and later) produce WITH optimizations.

    0 讨论(0)
  • 2020-12-18 19:29

    In my case when I had the optimizations flag turned on it would not complete all the operations so there were measuring points missing in the final result so I simply turned the optimization flag off to fix the bug:

    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++;
                                }
                            }
                        }
                    }
                }
            }
        });
    }
    

    Thus I replaced the Parallel.Invoke call with this as well. FYI, this problem occurred using .NET Framework 4.7.

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

    UPDATE:

    OK, I've found that I was able to re-enable the optimization flag and use Parallel.Invoke if I remove the async Task from the method signature:

        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)
                            );
    

    Alternatively, I think I could use Task.Run for each and then a await Task.WhenAll(t1, t2, t3); as explained here, but in this case I am not making explicit database calls so I don't think it applies to use Task.Run instead of Parallel.Invoke though this page does explain why my Parallel.Invoke was not completing: Parallel.Invoke does not wait for async methods to complete

    For details, please see "Concurrency in C#" https://stephencleary.com/book/

    0 讨论(0)
  • 2020-12-18 19:38

    You would normally use this option in a release build. It's safe and mainstream to do so. There's no reason to be afraid of releasing code with optimizations enabled. Enabling optimization can interfere with debugging which is a good reason to disable it for debug builds.

    0 讨论(0)
  • 2020-12-18 19:39

    It is possible that some bugs will occur when running in release mode that do not otherwise occur. The infamous "non-volatile flag" comes to mind:

    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
    

    This happens because of compiler optimizations, as the flag variable gets cached by the core of thread t and thus it cannot see the updated value of flag.

    0 讨论(0)
  • 2020-12-18 19:42

    .net compiler optimization could cause bugs. happend to me today. took me a few hours to nail it. the code is:

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

    at some point, the list[i+1] is addressed as list[i], as if both both point to the same item. this bug was so wierd. the code ran well at debug mode, and at release mode, but when I ran it out side visual studio, ex. from the .exe file, the code crashed. only turning off the compiler optimization fixed it.

    0 讨论(0)
提交回复
热议问题