摘抄某個位置的文章.URL忘記了.
在提取遊戲資源的時候,我編程用的是C++中的 ifstream , ofstream, 可是在讀寫檔案的時候速度很慢,硬碟狂轉,請問一下有人知道如何提高速度嗎?多謝!
| 悠久揚笛 |
2007-05-15 11:22 |
| 用記憶體映射方式可能會好點 |
|
| Asakura倉貓 |
2007-05-15 13:26 |
直接用win32 API試試如何 CreateFileA、ReadFile、WriteFile記住CreateFileA時dwFlagsAndAttributes參數至少要FILE_FLAG_RANDOM_ACCESS+FILE_FLAG_SEQUENTIAL_SCAN,這樣速度會快很多 |
|
| rednaxela |
2007-05-15 14:47 |
| 硬碟狂轉很正常.程度問題.讀寫大量資料的時候肯定得狂轉,但是實際的讀寫次數卻未必一樣.簡單說如果能用較少的讀寫次數完成較多的工作,那麼硬碟負荷會降低並且程式的速度也會加快.同樣是4K的資料一次寫出會比寫出4K次的1位元組會快非常多.相對的,減少硬碟讀寫次數意味著消耗更多的記憶體來作為緩衝. 傳說中C++標準庫裡的stream實現...這在高強度的讀寫操作時是個瓶頸.涉及大量檔案操作的ACM題向來會提示不要用C++標準庫裡的istream/ostream/iostream/ifstream/ofstream/fstream. Quote:
In general the stream classes should be pretty efficient, but performance can be improved further in applications in which I/O is performance critical.
效率低不是標準庫的錯...它本來設計是要實現出不錯的效能的.不過它在一般使用的時候還有許多你或許根本用不到的功能,例如說一些格式化的操作.於是就慢了.另外stream的buffering也可能造成效率問題;至於說為了I/O操作建立了一大堆對象...那個也罷.要想"修理"這些問題可以自己寫basic_streambuf<>的具體特化.或者直接使用stream buffer會稍微快一點: 像這樣直接寫出 Copy code
#include <iostream>
int main() { // copy all standard input to standard output std::cout << std::cin.rdbuf(); }
和這樣直接讀入 Copy code
#include <iostream>
int main() { // copy all standard input to standard output std::cin >> std::cout.rdbuf(); }
cin和cout不過是串連到stdin和stdout的istream和ostream執行個體.對ifstream和ofstream的執行個體也是一樣的. 不過...不用C++標準庫裡的stream或許更根本一些.在Windows上的話直接用Win32 API然後自己管理buffer對於簡單操作來說很順手.使用mapping也是一種不錯的方案.boost庫也有對mapping _file的支援. |
|
| joyful |
2007-05-15 15:05 |
Quote:
引用第1樓悠久揚笛於2007-05-15 11:22發表的 : 用記憶體映射方式可能會好點
記憶體映射?請問一下具體該怎麼做? |
|
| 悠久揚笛 |
2007-05-15 16:01 |
Quote:
引用第4樓joyful於2007-05-15 15:05發表的 :
記憶體映射?請問一下具體該怎麼做?
用CreateFileMapping() 具體可以參考msdn |
|
| Asakura倉貓 |
2007-05-15 16:15 |
| CreateFileMapping()在單線程時對效率提升不會很明顯恩,因為讀入和寫出磁碟的實際速度都差不多 這個API的最佳使用方式是在對一個不太大的檔案做多線程非同步作業,比如對一個檔案資料進行重排列,這樣效率提升才很明顯 其他方面的用處就是在操作一個檔案比操作記憶體更方便而又不能改變物理檔案的實際內容的情況,比如使用比較複雜的演算法對檔案進行加密解密,因為對這個函數產生的控制代碼進行操作並不會馬上寫入檔案 Quote:
引用第3樓rednaxela於2007-05-15 14:47發表的 : 硬碟狂轉很正常.程度問題.讀寫大量資料的時候肯定得狂轉,但是實際的讀寫次數卻未必一樣.簡單說如果能用較少的讀寫次數完成較多的工作,那麼硬碟負荷會降低並且程式的速度也會加快.同樣是4K的資料一次寫出會比寫出4K次的1位元組會快非常多.相對的,減少硬碟讀寫次數意味著消耗更多的記憶體來作為緩衝. .......
FX君你這個觀點有點小問題 雖然從硬碟壽命來說,寫入的次數的確是越少越好,但是對效率來說卻並非如此 實際操作多些就能發現,一個大檔案並不是以越少次數寫入就越快 每次寫入資料的最佳大小要依照硬碟緩衝大小來決定 如果寫的是一個要在別人機器上使用的通用程式,那麼就要考慮不確定的緩衝 現在硬碟一般緩衝為4M,考慮到還有其他程式在使用緩衝(比如QQ這個磁碟緩衝強盜),所以一般將資料分塊大小定為1M左右為好 再者就是要在寫入磁碟的函數之後馬上加一句等待系統的指令,這樣在硬碟完成寫入過程後程式才會繼續,這在提高效率上非常重要 然後就能發現,這樣寫入大檔案時速度絕對比一次寫入要快多而且也不會讓系統卡(當然你有啥演算法在佔用CPU的話就不能算數…… OTL) |
|
| joyful |
2007-05-15 17:50 |
| 那用 C 的 fscanf 和 fprintf 會不會好一點? |
|
| rednaxela |
2007-05-15 18:12 |
Quote:
引用第6樓フェイト於2007-05-15 16:15發表的 : 實際操作多些就能發現,一個大檔案並不是以越少次數寫入就越快 每次寫入資料的最佳大小要依照硬碟緩衝大小來決定 如果寫的是一個要在別人機器上使用的通用程式,那麼就要考慮不確定的緩衝 現在硬碟一般緩衝為4M,考慮到還有其他程式在使用緩衝(比如QQ這個磁碟緩衝強盜),所以一般將資料分塊大小定為1M左右為好
嗯,學到了~我平時寫出是用512K的緩衝,速度尚且可以(與單位元組寫出相比=_=),所以一直沒仔細想清楚這關係... Quote:
引用第7樓joyful於2007-05-15 17:50發表的 : 那用 C 的 fscanf 和 fpritf 會不會好一點?
C的FILE系列至少比C++現有標準庫裡的stream要快一些(可能是錯覺).FILE系列,fopen/fread/fgetc/fgets/fscanf/fseek/fwrite/fputc/fputs/fprintf/fclose,其實在底層也是有系統控制的緩衝.大小...我不知道.不過至少它不會像stream那樣需要建立很多個物件執行個體,相對來說效率應該是高些的.scanf類函數要注意泄漏問題就是了... |
|
| 悠久揚笛 |
2007-05-15 18:43 |
FILE自己維護了一套緩衝機制 FILE會使用預設的一個緩衝值作為io緩衝(4k),或者也可以通過setbuf來設定這個緩衝的大小假設你fread 1位元組 會導致ReadFile 4k,然後fread再將要讀取的資料copy到指定的緩衝區中。以後訪問只要不過這個邊界,就一直從該io緩衝中讀取,fwrite也是,直到超過io緩衝邊界才真正的調用WriteFile。可以調用flush主動強制重新整理從而調用WriteFile 或者fclose被動重新整理調用WriteFile(這時fclose會阻塞)。 再說一下硬碟的 硬碟的cache由硬碟控制器管理和使用 就像處理器的cache沒法直接操作一樣 寫硬碟的時候會先寫入cache 然後硬碟內部會把資料慢慢寫入磁碟 這個過程中沒有最佳化 也就是說硬碟驅動按什麼順序寫的 寫入磁碟就是什麼順序 而實際上 硬碟是個隨機訪問裝置 先寫哪個後寫哪個無所謂 所以一般在把應用程式層的io訪問轉化為底層的io請求後 核心層會做io請求最佳化排序 假設一個io隊列上目前掛著10個請求 核心層會事先計算每個請求在物理上的位置 然後進行排序 以保證磁頭轉動一周,盡量讓10個請求中的多個在一周內完成,想像一下 最好的情況 10個請求都在一個盤面上 磁頭旋轉1周 10個請求全部完成 最壞的情況 要轉10周 10周的原因是一次只能操作一個磁頭 而10個請求可能不幸的在10個盤面上(這時候核心也不會再排序了) 因此讓自己的io操作儘可能維持在連續的磁碟空間 且在物理上不跨越盤面 這樣效果最好。為此你可能需要硬碟的準確的參數 並精確計算。 緩衝的優勢在高強度的io操作會被抵消 因為硬碟的寫入速度始終跟不上處理器的請求 cache只能協助緩衝一下 cache越大 緩衝的時間越長 當cache填滿 硬體上ready訊號為無效 硬碟驅動不能再寫了 只能掛起核心的io隊列 這時候上層還在不停的請求 核心層要麼繼續往io請求隊列上掛裝請求 要麼阻塞發起io的進程 等到cache有空間了 硬體使能ready訊號 驅動重新從內河的io請求隊列上摘取io請求 再填cache 又滿 。。。。 也就是說cache的優勢只在一開始的緩衝時間上 這個優勢對於小的io請求特別有好處 因為能在填滿cache之前不會遭到阻塞或掛起 縱上所述 軟體上其實做的很有限而且也很累 何必呐 orz。。。。 |
|
| ravenex |
2007-05-31 03:38 |
| 想請教一下各位大大,不考慮資料在硬碟上的讀寫,僅考慮在記憶體的操作,那同樣大的資料區塊,以較小的單位,例如1位元組,或者以較大的單位,例如16位元組,分別做遍曆,速度的差異是什麼因素帶來的?假設要給一個資料區塊按位元組做固定的異或,可以在(1)unsigned char, (2)unsigned int, (3)unsigned long long等單位上做,同時假設機器上的通用寄存器是32位,那(2)肯定比(1)快,因為迴圈次數少了而機器碼長度幾乎一樣,但是(2)與(3)的差別在什麼地方? 還想請教一下,現在有很多硬碟裝在外接盒子裡,盒子上也有cache嗎?一般有多大?對硬碟的讀寫效能是否有影響? |
|
| 悠久揚笛 |
2007-05-31 10:22 |
| 首先unsigned long long 這個類型 我記得在vc下沒有 在gcc上有 代表64位長的資料類型 在32位機器上使用64位元據類型的話,結果就是編譯器對64位元據長的資料進行兩次訪問。 cahce的問題我上面解釋過了 對於高壓力的io操作 cache的大小作用沒那麼特別明顯了 但是對於輕量和中量的訪問 cache越大 阻塞硬碟驅動程式對硬碟的訪問的機會越少,速度自然越快。 軟體上所有對磁碟的訪問 最終都變成硬碟驅動程式對硬碟裝置的訪問。 硬碟是慢速裝置 對他寫入的時候 如果沒有cache 那麼每次對他訪問 他只有確切的完成後才允許驅動程式再訪問它,但是等待它完成io操作是很費時的 因此引入了cache 每次驅動程式將資料寫入硬碟的cache 因為cahce有一定容量 因此不用等待硬碟完成io操作 就可以繼續寫入 而另一方面硬碟從cache中取數 然後一次次的執行io操作 這樣cache就像個fifo cache越大 容量越多 驅動程式寫入被阻塞的可能性就越少 但是硬碟畢竟慢 cache也只是權宜之計 如果在cache被填滿前不再有請求了 那麼8M的cache就比2M的cache能緩衝更多的資料 自然速度更快 但是如果你的io請求資料量非常大 那麼8M也好 2M也好 總會被快速填滿 屆時2者就沒什麼分別了 因此cache大小對效能的影響只在輕量級和中量特別有效果。 |
|
| 悠久揚笛 |
2007-05-31 10:31 |
另外就是實際的效能還和很多因素相關 比如你用的讀寫函數 像fread、fwrite這樣的 都在使用者層實現了自己的io緩衝 你讀 實際上它會讀1塊(和檔案系統的塊的含義不同) 你寫1位元組 它只把資料寫入記憶體的io緩衝裡 而不實際寫入 另外就是檔案系統 因為檔案系統的基本單位不是位元組 而是塊 具體的塊大小 每個檔案系統都不同 比如你用ReadFile直接讀取1位元組 那麼檔案系統可能會讀取1個塊的資料 這樣檔案系統自己也有一個緩衝 再往下是核心提供的通用io緩衝 這個緩衝和具體檔案系統無關 只要有對io的訪問 就把資料緩衝在自己維護的基於頁的緩衝裡 最後就是硬體層級的cache了 其中檔案系統和核心的通用緩衝 這2個設計作業系統的實現 每個作業系統的具體實現方式可能不同。 |
|
| ravenex |
2007-05-31 15:26 |
| 那在32位機器上使用64位的資料類型並不會比使用32位的資料類型來執行固定的迴圈操作要快,是這樣的嗎?小的還是有點不明白,還得多多修鍊才行 多數遊戲的封包裡,單個檔案都不會達到4M吧?那研究一下cache的最佳使用經驗還是有意義。不然也不會出現B樹等的資料結構專門著眼於硬碟讀寫的特點 |
|
| 悠久揚笛 |
2007-05-31 18:49 |
Quote:
引用第13樓ravenex於2007-05-31 15:26發表的 : 那在32位機器上使用64位的資料類型並不會比使用32位的資料類型來執行固定的迴圈操作要快,是這樣的嗎?小的還是有點不明白,還得多多修鍊才行
多數遊戲的封包裡,單個檔案都不會達到4M吧?那研究一下cache的最佳使用經驗還是有意義。不然也不會出現B樹等的資料結構專門著眼於硬碟讀寫的特點
不 實際上會快 因為你無形中對2個4位元組的訪問放在一個迴圈裡做了 這個最佳化叫迴圈展開 這種最佳化通常編譯器做不到 是人為的最佳化 有資料顯示展開16-32(也就是一個迴圈內,每次處理64-128位元組,每次4位元組)能夠提高效能。(主要是充分利用了指令流水線) 我認為專門研究硬碟的cache意義不大 因為從你的程式到實際寫入硬碟 中間過了好幾層 而且不能只把硬碟當作你一個人使用的對象 因此最佳化的時候主要不是研究針對硬碟cache的最佳化 當然除非你跨過所有的軟體層 直接和驅動打交道(資料庫通常會這麼做) |
|
| ravenex |
2007-05-31 19:11 |
怪小的沒說清楚,是想說自己寫的程式所管理的buffer的大小,還是值得研究的 硬碟本身的cache,還有DMA什麼的,每層上都有緩衝,隔那麼遠也管不了了。像小的這樣唯寫應用的的確不需要關心那麼底層的實現 |
|