環境:
- window 10
- vs 2019 16.4.5
- .netcore 3.1.1
參照:
項目發佈Debug和Release版的區別
享受release版本發佈的好處的同時也應該警惕release可能給你引入一些莫名其妙的大bug
一、Releas版本相比Debug版本的性能提升很大
Debug模式在編譯時不對源代碼進行優化,而Release模式進行了大膽的優化,使得程序在代碼大小和運行速度上都有顯著提高。
下面通過一個對10000條數據進行冒泡排序的例子來比較它們二者的性能差距:
class Program
{
public static void Main(string[] args)
{
//準備測試數據
var len = 10000;
var datas = new int[len];
for (int i = 0; i < len; i++)
{
datas[i] = new Random().Next(1, 100000);
}
//冒泡排序5次
for (int i = 0; i < 5; i++)
{
var arr = new int[datas.Length];
datas.CopyTo(arr, 0);
Stopwatch watch = new Stopwatch();
watch.Start();
Order(arr);
watch.Stop();
Console.WriteLine(watch.Elapsed);
}
Console.WriteLine("ok");
Console.Read();
}
public static int[] Order(int[] datas)
{
for (int i = 0; i < datas.Length; i++)
{
for (int j = i + 1; j < datas.Length; j++)
{
if (datas[i] < datas[j])
{
int t = datas[i];
datas[i] = datas[j];
datas[j] = t;
}
}
}
return datas;
}
}
Debug模式的輸出:
Release模式的輸出:
從上滿可以看到,將近三倍的性能差距!!!
二、Releas版本可能會出現莫名的bug
雖然通過上面的對比可以看到Releas版本有着較大的性能優勢,但同時它也可能帶來了莫名的bug。上面說到,編譯器對代碼進行了大膽的優化,比如說將一些值直接讀取到cpu高速緩存中,這在多線程的操作環境中就可能帶來問題,下面看一個例子:
class Program
{
static int isStop = 0;
public static void Main()
{
var t = new Thread(() =>
{
var isSuccess = true;
while (isStop == 0)
{
isSuccess = !isSuccess;
}
});
t.Start();
Thread.Sleep(1000);
isStop = 1;
t.Join();
Console.WriteLine("ok");
Console.ReadLine();
}
}
Debug版本的輸出:
Release版本的輸出:
可以看到,Release版本沒有輸出,說明它死循環了沒有讀取到isStop 的改變值,這其實就是Release版本的優化導致的。Releas版本把isStop緩存到了CPU的告訴緩存中,導致主線程修改了isStop的值卻不能反饋到新線程上去。
那麼有沒有解決辦法呢?
- 辦法1:不要在多線程中這麼操作變量。
- 辦法2:不要使用Releas版本
- 辦法3:使用MemoryBarrier/VolatileRead
- 辦法4:使用volatile 關鍵字
其實,真正解決問題的就是辦法3和辦法4,那麼我們看看這兩個是什麼東西:
- VolatileRead:忽略CPU的高速緩存,獲取最新的數據,這句話執行後就會將isStop刷新,從下面的註釋中也可以看得出來
上面的代碼使用Release編譯後發現是正常的了。 - MemoryBarrier:參照:MemoryBarrier方法
使用上面的代碼處理後,Release再編譯也正常了。 - volatile關鍵字
這個關鍵字要用在類的字段上,如下所示:
這樣使用Release編譯後也正常了。
三、Release版本的調試問題
在調試中去掉"僅我的代碼“即可!
這樣,無論你是F5或者是附加到進程都是可以調試的。