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 brother about it and he said that it is unchecked for debugging and that in C++ it can potentially do things that would break or bug the code... but he doesn't know about C#.
So my question is, can I check this box for my release build without worrying about it breaking my code? Second, if it can break code, when and why? Links to explanations welcome.
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.
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).
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.
Should optimisations introduce bugs? No.
Could optimisations introduce bugs? Maybe, nothing's perfect after all.
Could optimsations uncover bugs that were always in your code, but are hidden when they are turned off? Absolutely, happens quite a bit.
The important thing is to realise that it's a change. Just like you'd test if you'd done a lot of changes, you should test when you turn them off. If final-release will have them turned on, then final-test must have them turned on too.
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.
Example wise i have a piece of code from some simulation parts of my master thesis. In which with the optimization flag turned on the code don't really break the program, but the pathfinder only performs one run and loops. (the recursive code traps itself in a loop on the pathfinder which it always breaks out of with the optimization flag turned off).
So yes it is possible for the optimization flag to make the software behave differently.
.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.
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/