2013年11月8日 星期五

關於效能改進,memory copy不是第一個要考量的

常常聽到有人說為了速度,要避免memory copy。但我在無論是維護性與效能上的考量,都覺得不是這麼回事。不要去追求pointer上的巧妙操作達到的zero copy,而忽略了平行處理上的可讀性。zero copy要作的地方是user與kernal space間的copy,而非同user space內的copy。後者幾乎是可以放在最後才去考量的。因此,我在評估系統設計對效能的衝擊有下面順序:
1. 減少 context switch:少用process,thread,少呼叫system call。使用 libev 這樣 event driven backend 來達到多工。而多處理器則使用 thread pool 的方式。
2. 增加 cache hit rate:在可攜的原始碼的原則下,採用那種記憶體也決定了cache的效率:
    1. 優先使用 function 內部定義的空間,因為它是位於 stack,只要function return,SP 暫存器一減回去,就相當於被釋放了,是極為有效率的。

    2. 如果要跨 function,就要訂好一個結構或是陣列,定義在 globol 的記憶體,因為它會位於固定的 .DATA 區段,當很密集在上面操作時,就很容易將資料保留在cache中。

    3. 最後才考慮使用 malloc 系列函式,取得的空間會位於 heap 裡面。常常有一些作法是使用動態配置去配出一塊塊的記憶體,然後用 pointer 將它們連起來成為某一種資料結構。下面文章就是在講這種方式的問題在哪裡。
http://www.codeproject.com/Articles/340797/Number-crunching-Why-you-should-never-ever-EVER-us#TOC_DA_IV

使用heap取得的記憶體會東一塊西一塊,一下在這個 page 一下在另一個,就比較不容易 cache hit。反觀如果配置靜態記憶體加上event driven,將資料與運算集中起來一次在user space處理掉,就會很有效率。

如果資料結構真的很龐大、所指向的資料又不一定大小,該如何靜態?
這時候,一樣可以設計一個 warm cache 與 cold cache。將你的資料結構的"節點"放在靜態的array,並且用你最擅長的point連結他們,這是warm cache,因此走訪節點的時候就很容易在cache就搞定。當你找到節點後,再取出point指向的資料,這就是cold cache。


當你都檢視了用了多少 process、thread,IPC的資料傳輸有多少,資料結構是否容易cache hit。這些問題都已經釐清以後,再去探討資料搬移造成了多少負荷才能真的增加速度。


沒有留言: