有些事情,同樣在使用 TPL 時也需要避免。第一件事情就是使用記憶體的 allocation。
原因
記憶體的 allocation 會導致 GC,記憶體的回收變成的一項負擔。
解決方法1: 使用 stack,避免GC。
範例程式如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace TPL_2 {
class Program {
static void Main(string[] args) {
Console.WriteLine("Sequence");
Console.WriteLine(Time(GoSeq));
Console.WriteLine("Parallel, using heap");
Console.WriteLine(Time(GoPalHeap));
Console.WriteLine("Parallel, using stack");
Console.WriteLine(Time(GoPalStack));
}
private static void GoSeq() {
var q = (from i in Enumerable.Range(1, 20000000)
select new { Value = i }).Max(i => i.Value);
Console.WriteLine(q);
}
//anonymous type, 會使用 heap。導致GC 回收記憶體,效能較差
private static void GoPalHeap() {
var q = (from i in ParallelEnumerable.Range(1, 20000000)
select new { Value = i }).Max(i => i.Value);
Console.WriteLine(q);
}
//struct, 會使用 stack,效能較快
private static void GoPalStack() {
var q = (from i in ParallelEnumerable.Range(1, 20000000)
select new MyClass { Value = i }).Max(i => i.Value);
Console.WriteLine(q);
}
struct MyClass {
public int Value;
}
static TimeSpan Time(Action a) {
Stopwatch w = Stopwatch.StartNew();
a();
return w.Elapsed;
}
}
}
執行結果
想不到吧!使用 Heap 反而使得 TPL 執行效果比不使用更差。改使用 struct 後就變快了。
解決方法2: 使用 Server GC
第二個方法是啟用 Server GC (伺服器記憶體回收),見<gcServer> 項目。簡單的說,使用這個方法,會讓CPU 的每個 Core 有獨立的 GC,因此回收的效能變快了。
.net framework 預設使用 workstation (工作站)的方式回收記憶體,也就是只有一個 GC。使用預設的 workstation 方式時, GC 必須考慮 multi-core 的 concurrency 問題,因此回收的效能較差。
我們加上一個 app.config ,如下
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<gcServer enabled="true" />
</runtime>
</configuration>
執行結果
由結果得知,加上設 gcServer 的設定,雖然仍然使用了 heap,速度還是變快了,但無法與使用 stack相比。
沒有留言:
張貼留言