17

解析する必要のある巨大なファイルをいくつか入手しました。ファイル全体をメモリ内に割り当てる必要がないため、人々はmmapを推奨しています。

しかし、「top」を見ると、ファイル全体をメモリに開いているように見えるので、何か間違ったことをしているに違いないと思います。「トップショー>2.1ギグ」

これは、私が行っていることを示すコードスニペットです。

ありがとう

#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <fcntl.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <cstring>
int main (int argc, char *argv[] ) {
  struct stat sb;
  char *p,*q;
  //open filedescriptor
  int fd = open (argv[1], O_RDONLY);
  //initialize a stat for getting the filesize
  if (fstat (fd, &sb) == -1) {
    perror ("fstat");
    return 1;
  }
  //do the actual mmap, and keep pointer to the first element
  p =(char *) mmap (0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
  q=p;
  //something went wrong
  if (p == MAP_FAILED) {
    perror ("mmap");
    return 1;
  }
  //lets just count the number of lines
  size_t numlines=0;
  while(*p++!='\0')
    if(*p=='\n')
      numlines++;
  fprintf(stderr,"numlines:%lu\n",numlines);
  //unmap it
  if (munmap (q, sb.st_size) == -1) {
    perror ("munmap");
    return 1;
  }
  if (close (fd) == -1) {
    perror ("close");
    return 1;
  }
  return 0;
}
4

8 に答える 8

41

いいえ、あなたがしているのはファイルをメモリにマッピングすることです。これは、実際にファイルをメモリに読み込むこととは異なります。

読み込んだ場合は、内容全体をメモリに転送する必要があります。マッピングすることで、オペレーティングシステムに処理させることができます。そのメモリ領域内の場所に対して読み取りまたは書き込みを行おうとすると、OSは最初に関連するセクションをロードします。ファイル全体が必要でない限り、ファイル全体はロードされません。

ここでパフォーマンスが向上します。ファイル全体をマップし、1バイトだけを変更してからマップを解除すると、ディスクI/Oがまったくないことがわかります。

もちろん、ファイル内のすべてのバイトに触れると、そうです。ある時点ですべてがロードされますが、必ずしも物理RAMに一度にロードされるとは限りません。ただし、ファイル全体を前もってロードする場合でも、これが当てはまります。システム内の他のプロセスのメモリと一緒に、すべてを格納するのに十分な物理メモリがない場合、OSはデータの一部をスワップアウトします。

メモリマッピングの主な利点は次のとおりです。

  • ファイルセクションの読み取りは、必要になるまで延期します(また、必要にならない場合は、ロードされません)。したがって、ファイル全体をロードするときに大きな初期費用はかかりません。積載コストを償却します。
  • 書き込みは自動化されているため、すべてのバイトを書き出す必要はありません。それを閉じるだけで、OSは変更されたセクションを書き出します。これは、メモリがスワップアウトされた場合にも発生すると思います(物理メモリが少ない状況で)。これは、バッファがファイルへの単なるウィンドウであるためです。

アドレス空間の使用量と物理メモリの使用量の間には、ほとんどの場合切断があることに注意してください。RAMが1Gしかない32ビットマシンでは、4Gのアドレス空間を割り当てることができます(理想的には、OS、BIOS、またはハードウェアの制限がある場合があります)。OSは、ディスクとの間のページングを処理します。

そして、明確化のためのあなたのさらなる要求に答えるために:

明確にするために。したがって、ファイル全体が必要な場合、mmapは実際にファイル全体をロードしますか?

はい。ただし、一度に物理メモリに保存されない場合があります。OSは、新しいビットを取り込むために、ビットをファイルシステムにスワップアウトします。

ただし、ファイル全体を手動で読み込んだ場合も同様です。これら2つの状況の違いは次のとおりです。

ファイルを手動でメモリに読み込むと、OSはアドレス空間の一部(データが含まれる場合と含まれない場合があります)をスワップファイルにスワップアウトします。また、ファイルを使い終わったら、手動でファイルを書き直す必要があります。

メモリマッピングを使用すると、元のファイルをそのファイル/メモリ専用の追加のスワップ領域として使用するように効果的に指示されています。また、そのスワップ領域にデータが書き込まれると、実際のファイルにすぐに影響します。したがって、完了時に手動で何かを書き直す必要はなく、通常のスワップに影響を与えることもありません(通常)。

これは実際にはファイルへの単なるウィンドウです。

                        メモリマップトファイルイメージ

于 2009-12-29T03:35:31.453 に答える
5

また、fadvise(2)(およびmadvise(2)、posix_fadviseおよびposix_madviseも参照)を使用して、マップトファイル(またはその一部)をread-onceとしてマークすることもできます。

#include <sys/mman.h> 

int madvise(void *start, size_t length, int advice);

アドバイスは、アドバイスパラメータに示されます。

MADV_SEQUENTIAL 

順番にページ参照を期待します。(したがって、指定された範囲のページは積極的に先読みでき、アクセス後すぐに解放される可能性があります。)

移植性:posix_madviseおよびposix_fadviseは、IEEE Std 1003.1、2004のADVANCED REALTIMEオプションの一部です。定数は、POSIX_MADV_SEQUENTIALおよびPOSIX_FADV_SEQUENTIALになります。

于 2010-02-07T02:58:55.760 に答える
3

top多くのメモリ関連の列があります。それらのほとんどは、プロセスにマップされたメモリスペースのサイズに基づいています。共有ライブラリ、スワップアウトされたRAM、およびmmapされたスペースを含みます。

列を確認してくださいRES。これは、現在使用されている物理RAMに関連しています。私は(確かではありませんが)mmapファイルを「キャッシュ」するために使用されるRAMが含まれると思います

于 2009-12-29T03:36:47.753 に答える
2

間違ったアドバイスが提供された可能性があります。

メモリマップトファイル(mmap)は、ファイルを解析するにつれて、ますます多くのメモリを使用します。物理メモリが少なくなると、カーネルは、LRU(最近使用されていない)アルゴリズムに基づいて、ファイルのセクションを物理メモリからマップ解除します。ただし、LRUもグローバルです。LRUは、他のプロセスにページをディスクにスワップさせ、ディスクキャッシュを減らすこともできます。これは、他のプロセスやシステム全体のパフォーマンスに深刻な悪影響を与える可能性があります。

行数を数えるなど、ファイルを直線的に読み取る場合、mmapはメモリを解放してシステムに戻す前に物理メモリをいっぱいにするため、不適切な選択です。一度にブロックをストリーミングまたは読み取る従来のI/Oメソッドを使用することをお勧めします。そうすれば、メモリをすぐに解放できます。

ファイルにランダムにアクセスしている場合は、mmapが適切な選択です。ただし、カーネルの一般的なLRUアルゴリズムに依存しているため、最適ではありませんが、キャッシュメカニズムを作成するよりも使用する方が高速です。

一般に、複数のプロセスやスレッドから同時にファイルにアクセスする場合や、使用可能な空きメモリの量に比べてファイルが小さい場合など、パフォーマンスの極端な場合を除いて、mmapの使用はお勧めしません。

于 2009-12-29T04:19:44.347 に答える
1

「ファイル全体をメモリに割り当てる」と、2つの問題が発生します。1つは、割り当てる仮想メモリの量です。もう1つは、ファイルのどの部分がディスクからメモリに読み込まれるかです。ここでは、ファイル全体を格納するのに十分なスペースを割り当てています。ただし、実際にディスク上で変更されるのは、タッチしたページのみです。また、mmapが割り当てたメモリ内のバイトを更新すると、プロセスで何が起こっても正しく変更されます。mmapの「size」および「offset」パラメーターを使用して一度にファイルのセクションのみをマッピングすることにより、より少ないメモリーを割り当てることができます。次に、マッピングとマッピング解除を行って、ファイル内でウィンドウを管理する必要があります。おそらく、ファイル内でウィンドウを移動します。大量のメモリを割り当てるには、かなりの時間がかかります。これにより、アプリケーションに予期しない遅延が発生する可能性があります。プロセスがすでにメモリを大量に消費している場合は、仮想メモリが断片化されている可能性があり、要求したときに大きなファイルに十分な大きさのチャンクを見つけることができない可能性があります。したがって、できるだけ早くマッピングを実行するか、必要になるまで十分な大きさのメモリを使用できるようにするための戦略を使用する必要がある場合があります。

ただし、ファイルを解析する必要があることを指定しているので、データのストリームを操作するようにパーサーを編成して、これを完全に回避してみませんか?次に、ファイルの個別のチャンクをメモリにマップする必要はなく、先読みと履歴が最も必要になります。

于 2009-12-29T04:16:35.193 に答える
0

システムは確かにすべてのデータを物理メモリに入れようとします。あなたが節約するのはスワップです。

于 2009-12-29T03:35:42.997 に答える
0

ファイル全体を一度にメモリにマップしたくない場合は、mmap呼び出しでファイルの合計サイズよりも小さいサイズを指定する必要があります。オフセットパラメータと小さいサイズを使用すると、大きいファイルの「ウィンドウ」に一度に1つずつマップできます。

解析がファイルのシングルパスであり、ルックバックまたはルックフォワードが最小限である場合、標準ライブラリのバッファ付きI/Oの代わりにmmapを使用しても実際には何も得られません。ファイル内の改行を数える例では、fread()を使用してそれを行うのと同じくらい高速です。ただし、実際の解析はもっと複雑だと思います。

一度にファイルの複数の部分から読み取る必要がある場合は、複数のmmap領域を管理する必要があり、すぐに複雑になる可能性があります。

于 2009-12-29T04:20:44.280 に答える
0

少しオフトピック。

私はマークの答えに完全には同意しません。実際mmapにはより高速ですfread

システムのディスクバッファを利用しているにもかかわらずfread、内部バッファもあり、さらに、データは呼び出されたときにユーザー提供のバッファにコピーされます。

それどころか、mmapシステムのバッファへのポインタを返すだけです。したがって、2つのメモリコピーを節約することがあります。

しかしmmap、少し危険な使用。ポインタがファイルから出てこないことを確認する必要があります。そうしないと、セグメンテーション違反が発生します。この場合、fread単にゼロを返しますが

于 2009-12-29T04:55:04.323 に答える