FC2ブログ

PCで: mmap(2)の効能

 私は、テレビをLinuxマシンで録画して、それをそのLinuxマシンとか別のWindowsマシンとかで見ています。
 その、見るためのツールとしては主にVLCを使っています。まあ、BDを見るときにはWindowsのPowerDVDを使っていますが。VLCだと、なんか性能が出ないし、トリックプレイでおかしくなるんですよね。後者の問題は、WinXPがBDなんかの新しいUDFに対応していなくて、どっかから拾ってきたのを入れてなんとかしているからかも知れません。

 ところが最近、LinuxマシンでVLCを使っているとたまにおかしくなるんです。
 多分、OSの設定を大きく変更したので、その過程でなんかへくったんだろうと思います。VLCでなんか見ていて、ポーズをかけて別の作業をしたりすると、ハングアップしたりするんですよね。OS自体が。
 いきなりではなく徐々におかしくなるので、様子を見てみると、どうもVLCがメモリをバカ食いしているみたいです。

 OSの方をきちんとメンテする必要もあるかも知れませんが、当面VLCをなんとかしようと(他は特に問題ないので)、使用するメモリの上限みたいな設定項目がないか調べてみました。
 そしたら、それよりも先に、「メモリマップドファイル入力」という項目を見付けました。「ファイルメモリーマップの使用」というチェックボックスがひとつあるだけですけど。
 というわけでそこにチェックを入れて使ってみたら、使用メモリが激減しました。
 当面は、これで凌げるんじゃないかと予測してみたり。

 さて、この「メモリマップドファイル入力」ですが、多分、動画などのファイルにアクセスするときに、read()ではなくmmap()を用いる、ということなのではないかと思うのです。
 というか、あんなにメモリを食うとか、read()したデータを一体どう管理しているんだろう?

 mmap()というのは、指定したファイルの指定した部分を、プログラム(この場合はVLC)のアドレス空間にそのまま見せてあげる、という機能です。あるアドレスを見に行くと、そこにファイルの中身が見える、という感じです。
 これ、仕組みを理解していない人が結構多いのではないでしょうか。ファイルを読み込んで展開するから遅いんじゃないか、とか思っている人、多そうな気がします。
 私は、これ、性能面で随分有利な方式だと思うんですけどね。

 ちょっと解説してみます。話を単純化するために、細かいところは結構省きますが。

 まず、仮想記憶の話から始めましょう。
 コンピュータの中枢であるCPUが主記憶領域であるメモリにアクセスするとき、どのように見えているか?
 メモリは、現在一般的なパソコンでは、1バイト(8ビット)のデータを格納できる領域が一次元にずらーっと並んだように見えています。そして、各領域に数字でアドレスが付いています。
 例えば、0x1000番地("0x"はC言語などで16進数を示す印)には0x4aという数字が入っている、とか。
 0x0番地から始まって、32ビットマシンだったら0xffffffff番地まであって、この辺りにはプログラム本体、この辺りにはデータ、この辺りにはスタック、とかいう感じにわけて使っています。

 ところで、今のパソコン、色んなプログラムが同時に立ち上がっていますよね。それぞれが一次元のメモリ領域に混在していると、あっちのプログラムの情報がこっちに見えるセキュリティの問題とか、あるプログラムのバグが別のプログラムをぶち壊すとかいう安全面での問題とか、色々困るわけです。
 あともう一つ。
 実際にコンピュータに載せられるメモリの量は限られていますが、もっと使いたいという要望はどうしてもでてきます。

 というわけで、仮想記憶という機能があるのです。
 CPUの中心部とメモリの間に特殊な回路が用意してあります。で、0x1000番地をアクセスした、というつもりでいたら実際には0x8000番地にアクセスしていた、なんてことができるようになっています。
 この変換のための情報をまとまりにして、たまに切り替えてあげるとどうなるか?
 あるとき、0x1000番地を見に行ったら、実際には0x4000番地に行っていた。別のときには0x8000番地を見に行っていた。なんてことが可能になるわけです。
 このセットを、プログラム(ワープロとブラウザとか)毎に用意してあげれば、それぞれのプログラムが、0x0番地から上限まで(アドレス空間とか言います)を占有しているような気分になれるわけです。
 勿論、コンピュータ全体を管理するOSが各アプリケーションプログラムに見せたくないものを隠すこともできるわけです。

 ここで、ある番地をアクセスしたとき、どこにも割り当てておかないとOSに通報されるようにすることができます。これを使って、そういう事態が発生したら、ハードディスクからメモリに読み込んできて、割り当て情報を変更し、以後はそこをアクセスしたらディスクから読み込んだデータがある場所が見えるようにすることもできるわけです。
 これが、上記の二つ目の要望、大量のデータを扱いたいときのための仕組みですね。

 メモリマップドファイル入力(スペルが"mapped"だと、最後は「ド」ではなく「ト」だと思うんですけど)は、これを使っています。
 つまり、プログラムがあるアドレスにアクセスしたら、そのとき、ファイルからデータを読んでくるようにしてあげればいい。
 でも、なんで私はそれが性能向上になると思っているのか?

 では、ファイルを普通に読むときの流れを見てみます。
 古いUNIXの知識ですが、今話題にしているLinuxでもそう違わないでしょう。

 アプリケーションプログラムが、OSに対して、read()というシステムコール(OSの機能の呼び出し)を発行します。すると、OSは、アプリケーションが事前に発行していたopen()システムコールのときにOSの内部のアドレス空間(カーネル空間)にファイルを割り当てた(マップした)領域をアクセスします。すると、上記の機能が動いて、ディスク内のファイルのデータがメモリ上に読み込まれます。
 これを、read()で指定された、アプリのアドレス空間の該当する部分にコピーしてあげます。
 これで完了。
 ここで、OSの空間からアプリの空間(ユーザ空間)への転送が入るわけです。コピーそのものも時間がかかるし、別の空間へのコピーなので、それなりに面倒くさいし。

 ところが、mmap()を使うと、カーネル空間からユーザ空間へのコピーが省けるわけです。
 これが、私がmmap()だと性能向上が見込まれると考える理由です。

 実は、私がmmap()を使いたいと思った理由はまだあります。
 カーネル空間からユーザ空間へコピーしたということは、それらは別物で、二重のメモリ領域を消費します。
 メモリが潤沢にあれば別にかまわないのですが、これが不足してきたりすると、例えば、カーネル空間にディスクから読み込んだものは要するにディスク内のデータと同じなので、捨ててしまっても構わない。で、そのメモリを別のことに使い回したりします。
 しかし、ではまたあとで同じところにアクセスしたいということになったときには、前のが残っていればそのまま使えばいいんですけど、捨ててしまったらまた読み込まなければいけない。
 場合によっては、ユーザ空間の方をディスクの特別な領域に書き込んで退避して、空いたメモリを使い回したりします。
 メモリとディスクではアクセス速度に雲泥の差があるので、これが起きると目に見えて性能が落ちるわけです。

 私が使っているLinuxマシンはもう結構な年代物で、メモリが2GBしかありません。
 テレビ番組を録画したデータとかは平気で数GBになるので、メモリの使用効率は気にしないと。
 というわけで、mmap()を使えるんなら使いたい、と考えるわけです。

 ちなみに、先日までここで公開していたtspeeperも、可能な限りmmap()を使うようにしていました。

 こういう仕組みは、まだそうそう画期的な改善がされているという気もしないし(現役を退いて結構経つので見落しているかも知れませんが)、あまりドラスティックに変えると、既存のプログラムの性能が大きく変動したりするのでそうそう手を入れられないと思うし。
 というわけで、多分前述のVLCの設定は有効でしょう。

 ただ、Windows版のVLCにはこの設定項目ありませんでしたけど。

コメント

非公開コメント

プロフィール

水響俊二

Author:水響俊二
水響 俊二 [MIZUKI Shunji]

暫定的に、18禁作品の感想などは裏サイトで書いています。
   

最新記事
最新コメント
カテゴリ
検索フォーム
リンク
RSSリンクの表示
月別アーカイブ
アクセス解析中