2009-06-27 90 views
9

我在嘗試優化使用mmap處理大型數據集。數據集是在千兆字節範圍內。這個想法是將整個文件映射到內存中,允許多個進程同時處理數據集(只讀)。它雖然沒有按預期工作。Linux/perl mmap性能

作爲一個簡單的測試,我簡單地mmap文件(使用perl的Sys :: Mmap模塊,使用我認爲直接映射到底層C函數的「mmap」子),並讓進程處於睡眠狀態。在執行此操作時,代碼花費超過一分鐘才從mmap調用返回,儘管此測試無法執行任何操作 - 即使是讀取操作,也不會執行mmap的文件。

猜測,雖然也許linux需要整個文件在第一次mmap化時被讀取,所以在文件在第一個進程中映射(當它正在休眠時)之後,我在另一個進程中調用了一個簡單的測試,試圖讀取文件的前幾兆字節。

令人驚訝的是,似乎第二個過程在從mmap調用返回之前花費了大量的時間,大約與第一次mmap處理文件的時間相同。

我已經確定MAP_SHARED正在被使用,並且第一次映射文件的過程仍然是活動的(它沒有終止,並且mmap沒有被映射)。

我期望一個mmapped文件可以讓我給多個工作進程有效地隨機訪問大文件,但是如果每個mmap調用都需要先讀取整個文件,那麼這有點困難。我還沒有測試過使用長時間運行的進程來查看第一次延遲後訪問是否快速,但我期望使用MAP_SHARED,而另一個單獨的進程就足夠了。

我的理論是,mmap會立即返回,linux會根據需要或多或少地加載塊,但是我看到的行爲是相反的,表明它需要讀取每個文件的整個文件調用mmap。

任何想法我做錯了,或者如果我完全誤解了mmap應該如何工作?

回答

15

好的,發現問題了。正如所懷疑的,無論是linux還是perl都是怪罪。要打開和訪問文件,我做這樣的事:

#!/usr/bin/perl 
# Create 1 GB file if you do not have one: 
# dd if=/dev/urandom of=test.bin bs=1048576 count=1000 
use strict; use warnings; 
use Sys::Mmap; 

open (my $fh, "<test.bin") 
    || die "open: $!"; 

my $t = time; 
print STDERR "mmapping.. "; 
mmap (my $mh, 0, PROT_READ, MAP_SHARED, $fh) 
    || die "mmap: $!"; 
my $str = unpack ("A1024", substr ($mh, 0, 1024)); 
print STDERR " ", time-$t, " seconds\nsleeping.."; 

sleep (60*60); 

如果測試代碼,有喜歡的那些我在最初的代碼中發現沒有延遲,並創造了最小的樣品後,(都是那樣做的,正確的!)原因突然變得明顯。

錯誤在於我在我的代碼中將$mh標量當作句柄來處理,它的重量輕,可以輕鬆移動(讀取:按值傳遞)。原來,它實際上是一個GB長字符串,絕對不是你想要移動而不創建顯式引用(perl lingua爲「指針」/句柄值)的東西。因此,如果您需要以散列形式或類似形式存儲,請確保您存儲\$mh,並且在需要使用${$hash->{mh}}時將其解壓縮,通常作爲substr或類似的第一個參數。

+3

+1以瞭解詳細說明。 – RichieHindle 2009-06-27 21:06:25

+3

使用3 arg形式的open()。 – 2009-06-28 03:55:23

0

這聽起來令人驚訝。爲什麼不嘗試純C版本?

或者在不同的OS/Perl版本上嘗試您的代碼。

+0

我已經看了perl操作系統界面,它直接或多或少地調用C版本,但除非我弄清楚,否則我可能會測試一個C版本。 至於OS/Perl版本,我已經在兩個x86_64系統上進行了測試。一個是Ubuntu 8.04.2(linux 2.6.24-22,perl 5.8.8)和另一個Ubuntu 9.04(linux 2.6.28-13,perl 5.10.0)。同樣的行爲。第二個系統是一臺筆記本電腦,當我從我的測試中調用mmap時,我可以確定地確認存在嚴重的磁盤IO。 – 2009-06-27 14:36:09

8

如果你有一個相對較新版本的Perl,你不應該使用Sys :: Mmap。您應該使用PerlIO的mmap圖層。

你可以發佈你使用的代碼嗎?

+0

同意,PerlIO mmap圖層可能更受歡迎,因爲它也允許相同的代碼通過簡單地添加/刪除mmap屬性來使用/不使用mmap來運行。無論如何,我發現問題,發佈代碼,解決問題。 – 2009-06-27 15:12:03

+0

將問題解決到2GB。對於較大的文件perl仍然有問題,請參閱我的與此相關的其他答案。 – 2009-06-29 11:04:11

+0

PerlIO的mmap層是否可用於訪問/ dev/mem塊的讀/寫? – donaldh 2013-11-25 23:39:40

3

在32位系統上,mmap() s的地址空間相當有限(因操作系統而異)。請注意,如果您使用的是多GB文件,而您只能在64位系統上進行測試。 (我本來希望在評論中寫這個,但是我還沒有足夠的聲望點)

0

請參閱Wide Finder以獲得permap的mmap性能。但是有一個很大的缺陷。如果您的數據集將採用傳統HD,並且您將從多個進程中讀取數據,則您可以輕鬆進入隨機訪問,並且IO將降至不可接受的值(20至40倍)。

1

有一點可以幫助表現的是使用'madvise(2)'。可能最容易通過Inline :: C完成 。 'madvise'可以讓你告訴內核你的訪問模式是什麼樣的(例如順序,隨機等)。

0

好的,這是另一個更新。使用Sys :: Mmap或PerlIO的「:mmap」屬性都可以在perl中正常工作,但只能達到2 GB文件(魔術32位限制)。一旦文件超過2 GB,將出現以下問題:

使用Sys :: Mmap和substr來訪問該文件,似乎substr只接受位置參數的32位int,即使在系統中perl支持64位。有至少一個錯誤貼吧:

#62646: Maximum string length with substr

使用open(my $fh, "<:mmap", "bigfile.bin"),一旦文件大於2 GB,似乎perl的將掛起/或堅持閱讀第一讀取整個文件(不確定哪一個,我從來沒有跑過它足夠長的時間來看它是否完成),導致性能下降緩慢。

我還沒有找到任何解決方法,其中任何一個,我目前堅持緩慢的文件(非mmap'ed)操作處理這些文件。除非找到解決方法,否則我可能不得不使用C語言或其他更高級別的語言來實現處理,以更好地支持mmap處理大型文件。

0

如果我可以插入我自己的模塊:我會建議使用File::Map而不是Sys::Mmap。它比Sys :: Mmap更容易使用,並且不易崩潰。

0

您對該文件的訪問權限最好是隨機的,以證明完整的mmap。如果你的使用不是均勻分佈的,你可能更適合尋求,讀到一個新鮮的混合區域,然後進行處理,免費,漂洗和重複。並與4k的倍數的大塊工作,說64k左右。

我曾經對很多字符串模式匹配算法進行了基準測試。對整個文件進行整理是慢而毫無意義的。讀到一個靜態的32kish緩衝區更好,但仍不是特別好。通過閱讀新鮮的malloced塊,處理它,然後讓它去使內核在引擎蓋下運行奇蹟。速度上的差異是巨大的,但是然後再次模式匹配複雜性非常快,並且必須比通常需要更多地強調處理效率。