2010年11月7日 星期日

TPL 學習日記(9): 使用TPL 應避免的事情1: Avoid Memory Allocation

有些事情,同樣在使用 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;
    }
  }
}
執行結果

image

想不到吧!使用 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>

執行結果

image

由結果得知,加上設 gcServer 的設定,雖然仍然使用了 heap,速度還是變快了,但無法與使用 stack相比。

沒有留言:

Share with Facebook