2010-07-28 33 views
3

我正致力於調整最終爲最終用戶Web體驗提供服務的高性能,高容量數據引擎的性能。具體而言,委託給我的作品圍繞表徵多線程文件IO和將數據存儲到本地緩存的映射進行。在編寫測試應用程序以隔離時間高點時,有幾個問題已經暴露出來。代碼已被最小化,僅執行系統文件打開(open(O_RDONLY))調用。我希望這個查詢的結果可以幫助我們理解基本的底層系統過程,從而可以理解完整的預測性(或至少是關係型)時序模型。建議永遠受歡迎。我們似乎遇到了時間障礙,並希望瞭解行爲並確定是否可以打破這種障礙。用於打開文件的Linux多線程性能增強()

測試程序:

  1. 是用C,編譯使用GNU C編譯器如下面說明的;
  2. 最低限度編寫將發現的問題隔離到單個系統文件「open()」;
  3. 可配置爲同時啓動所請求數量的pthread;
  4. 加載大小約爲8K的1000個文本文件的列表;
  5. 創建線程(簡單地)沒有屬性修改;
  6. 每個線程都會從預先確定的文件列表中對下一個可用文件執行多個連續的文件open()調用,直到文件列表耗盡,這樣一個線程應該打開所有1000個文件,2個線程應該理論上打開500個文件(尚未證實)等);

我們已經多次運行測試,參數化地改變線程數,文件大小以及文件是否位於本地或遠程服務器上。有幾個問題出現了。

觀察到的結果(開口遠程文件):

  1. 文件打開時間是更高的第一次通過(如預期的,由於文件高速緩存);
  2. 使用一個線程運行測試應用程序以加載所有遠程文件需要X秒;
  3. 看來,在機器上可用CPU數量介於1和#之間的情況下運行應用會導致與CPU數量(nX秒)成比例的時間。
  4. 使用線程數> #CPU運行應用程序會導致運行時間似乎達到與使用#CPU線程運行所花費的時間大致相同的值(這是巧合還是系統限制,或者是什麼?)。
  5. 運行多個併發進程(例如,同一測試應用程序的25個併發實例)會導致所選時間與選定線程數的進程數大致成線性關係。
  6. 在不同的服務器上運行的應用程序顯示了類似的結果

觀察到的結果(打開本地駐留文件)的幅度更快倍

  1. 訂單(如所預期);
  2. 隨着線程數的增加,在4-5個活動線程中出現低定時拐點,然後再次增加,直到線程數等於CPU數量,然後再次停止;
  3. 運行多個併發進程(相同測試)會導致時間與恆定線程數(與上述#5結果相同)的進程數大致成線性關係。

此外,我們注意到本地打開大約需要0.01毫秒,而順序網絡打開速度是1毫秒慢100倍。打開網絡文件後,我們得到8線程的線性吞吐量增加8倍,但9 +線程無所作爲。在超過8個併發請求後,網絡開放調用似乎會被阻止。我們預期的初始延遲等於網絡往返,然後與本地吞吐量大致相同。也許在本地和遠程系統上執行額外的互斥鎖,需要100倍的時間。也許有遠程調用的一些內部隊列中只持有8

預期的結果和問題無論是測試或答案從論壇,像這樣的回答:

  1. 運行多個線程將導致在較短時間內完成相同的工作;
  2. 是否有最佳數量的線程;
  3. 線程數量和可用CPU之間是否存在關係?
  4. 是否存在觀察到8-10個文件限制的其他系統原因?
  5. 系統調用「open()」如何在多線程進程中工作?
  6. 每個線程獲取其上下文切換時間片;
  7. open()調用是否阻塞並等待文件打開/加載到文件緩存中?或者該操作是否允許在操作正在進行時發生上下文切換?
  8. 當open()完成後,調度程序是否會重新確定該線程的優先級,以便更早執行,或者線程是否必須等待輪到它才能輪到;
  9. 將1000個文件所在的裝入卷設置爲只讀還是讀/寫有區別?
  10. 當使用完整路徑調用open()時,路徑stat()中的每個元素都是ed?在文件樹列表中打開()一個公共目錄,然後用相對路徑打開()該公共目錄下的文件會更有意義嗎?

開發測試設置:

Red Hat Enterprise Linux Server release 5.4 (Tikanga) 

8-CPUS, each with characteristics as shown below: 

processor  : 0 
vendor_id  : GenuineIntel 
cpu family  : 6 
model   : 23 
model name  : Intel(R) Xeon(R) CPU   X5460 @ 3.16GHz 
stepping  : 6 
cpu MHz   : 1992.000 
cache size  : 6144 KB 
physical id  : 0 
siblings  : 4 
core id   : 1 
cpu cores  : 4 
apicid   : 1 
fpu    : yes 
fpu_exception : yes 
cpuid level  : 10 
wp    : yes 
flags   : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm syscall lm constant_tsc pni monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr sse4_1 lahf_lm 
bogomips  : 6317.47 
clflush size : 64 
cache_alignment : 64 
address sizes : 38 bits physical, 48 bits virtual 
power management: 

GNU C compiler, version: 
gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-46) 
+0

老兄,這是一大堆問題。請閱讀格式化順便說一句,使其更具可讀性。 – mvds 2010-07-28 17:41:39

+0

如果允許你插入基準圖,它也會非常有用。 – stsquad 2010-07-30 13:20:21

回答

1

不知道這是你的問題之一,但它可能是有用的。

在單個SATA磁盤上優化數千個隨機讀取的同時,讓我感到震驚的一件事是,在Linux中以非乾淨的方式執行非阻塞I/O並不容易,無需額外的線程。

在塊設備上(當前)不可能發佈非阻塞read();即它將阻塞磁盤需要的5ms尋道時間(並且5ms是永久的,在3GHz)。指定O_NONBLOCKopen()僅用於向後兼容的一些目的,使用CD刻錄機或其他東西(這是一個相當模糊的問題)。通常,open()不會阻塞或緩存任何內容,主要是爲了稍後處理文件以執行一些數據I/O。

出於我的目的,mmap()似乎讓我儘可能接近磁盤的內核處理。使用madvise()mincore()我能夠充分利用磁盤的NCQ功能,這可以通過改變未完成請求的隊列深度來證明,這與發出10k次讀取所花費的總時間成反比。

感謝64位內存尋址,使用mmap()將整個磁盤映射到內存完全沒有問題。 (在32位平臺上,您需要使用mmap64()來映射您需要的磁盤部分)