4

MacOS X マシンで次の C コード (2GB ファイル上の一連の mmaps と munmaps) を実行すると、Linux マシンよりも劇的に遅くなるようです。

#define BUFSZ 2000000000
static u_char buf[BUFSZ];
....

// Time 10000 mmaps and munmaps from random offsets for various 
// sizes of mapped chunk.
for (msize = 4096; msize <= 1048576; msize *= 16) {
  fd = open("io_benchmark.dat", O_RDONLY);
  if (fd  < 0 ) die("can't open io_benchmark.dat for reading");
  for (i = 0; i < 10000; i++) {
    // Make sure the block to be mapped doesn't start in the
    // last meg.
    offset = (size_t) random() % (BUFSZ - 1048576);
    mblock = femmap(fd, (off_t)offset, (size_t) msize, PROT_READ, 
                    "test block");
    total = 0;
    for (j = 0; j < msize; j++) {
      total += mblock[j];
    }
    femunmap(mblock, (size_t) msize, "test block");
  }
  printf("Elapsed time to mmap and munmap 10000 blocks of %d kB: %.4f sec\n", 
         msize/1024, (time = time_since_last_call()));

  rslt = close(fd);
  if (fd  < 0 ) die("can't close io_benchmark.dat after reading");
}

具体的には、2 つのマシンを比較します。

CPU     Xeon E3113 dual core @ 3.00GHz           Core 2 Duo @ 2.4GHz dual core
RAM     8GB                                      4GB
Kernel  2.6.18-92.el5PAE SMP i686                MacOS 10.6.4 Snow Leopard
Disk    WD 250GB SATA 16MB cache 7200 RPM EXT3   Hitachi 250GB SATA 5400 RPM, journaled HFS+

次の結果が得られます

                            Linux    MacOS X
Time for 10000 4kB mmaps    0.0165   682.87
Time for 10000 64kB mmap    0.0170   657.79
Time for 10000 1MB mmaps    0.0217   633.38

メモリの量が減ったことを考慮しても、ファイルが物理メモリの半分しかないことを考えると、これは珍しいことのようです。パフォーマンスを向上させる可能性のあるコードの変更または構成の変更を指摘できる人はいますか?

mmap の代わりに読み取りを使用してみましたが、かなりの違いがありますが、それを行うには、既存のコード ベースを大幅に変更する必要があります (そして、mmap は Linux での読み取りよりもはるかに高速です)。

4

3 に答える 3

4

あなたは正しいことを測定していないと思います。テストの内部を確認したところ、私のバージョンの gcc はループを完全に最適化することができました。

mblockこれは、たとえば、ポインターがデータへのポインターであると宣言したときに変更されvolatileます。次に、コンパイラは、ループ内のステートメントに対してすべての副作用を実行する必要があります。特に、メモリからチャージする必要があります。

したがって、テストから導き出せる唯一の結論は次のとおりです。

  • MacOS X のコンパイラはあまりスマートではありません
  • ベンチマークが生成するアセンブラを常にチェックする

したがって、実際にテストをやり直すことができれば、その機能に関して2つのシステムの実際の違いを見てみたいと思います.

于 2010-11-07T12:59:50.527 に答える
2

これは「設計による」ようです。このhttps://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemAdvancedPT/MappingFilesIntoMemory/MappingFilesIntoMemory.html#//apple_ref/doc/uid/TP40010765-CH2-SW1によると:

次の状況では、ファイル マッピングを使用しないでください。

  • ファイルを最初から最後まで順番に 1 回だけ読み取りたい。

  • ファイルのサイズは数百メガバイト以上です。(大きなファイルをマッピングすると、仮想メモリ空​​間がすぐにいっぱいになります。さらに、プログラムがしばらく実行されていたり、メモリ空間が断片化されている場合、プログラムに使用可能な領域がない場合があります。)

大規模なシーケンシャル読み取り操作の場合は、ディスク キャッシュを無効にして、ファイルを小さなメモリ バッファーに読み込むことをお勧めします。詳細については、「ファイルを選択的にキャッシュする」を参照してください。


問題を示すコード スニペットを次に示します。

off_t       file_target = off_t(3) << 29; //  1.5GB
const char* path = "B9361194.data";

//  Touch the output file
{
    FILE*       fp = fopen( path, "a");
    fclose(fp);
}

//  Open the output file
FILE*       fp = fopen( path, "rb+");
int         fd = fileno(fp);
off_t       file_physical = 0;
off_t       file_logical = 0;
ftruncate( fd, file_physical );

//  Declare the mapping
off_t       map_start = 0;
off_t       map_count = 0;
char*       map_address = 0;

//  Set up the input buffer.
//  We are just going to write this out until we have written total bytes
size_t      requested = 1024;
char    input[requested];
for ( size_t i = 0; i < requested; ++i ) input[i] = 1;

//  Write the buffer, resizing and mapping as we go.
while ( file_logical < file_target ) {
    //  Figure out how much to write.
    size_t limit = requested;
    if ( ( file_target - file_logical ) < (off_t)limit ) 
        limit = size_t( file_target - file_logical );

    //  If we can't fit the buffer inside the allocation
    //  unmap and grow everything
    if ( file_logical + (off_t)limit > file_physical ) {
        //  Unmap
        if ( map_address ) munmap( map_address, map_count );

        //  Grow the file by 64K
        off_t   new_physical = off_t(1) << 16;  //  64K allocation
        if ( new_physical < (off_t)limit ) new_physical = limit;
        file_physical += new_physical;
        ftruncate( fd, file_physical );

        //  Map the end
        map_count = off_t(1) << 23;    //  8MB
        if ( map_count > file_physical ) map_count = file_physical;
        map_start = file_physical - map_count;
        void* address = mmap( 0, map_count, ( PROT_WRITE | PROT_READ ), MAP_SHARED, fd, map_start );
        // int err = errno;
        // if ( address == MAP_FAILED ) CPPUNIT_ASSERT_EQUAL_MESSAGE( strerror(err), 0, errno );
        map_address = reinterpret_cast<char*>( address );
    }

    //  Copy the buffer in
    size_t  offset = size_t(file_logical - map_start);
    memcpy( map_address + offset, input, limit );
    file_logical += limit;
}

//  Clean up
if ( map_address ) munmap( map_address, map_count );
ftruncate( fd, file_logical );
fclose( fp );

彼らがそれを修正するかどうか、いつ修正するかはわかりません。

于 2011-04-29T21:40:05.440 に答える
1

OS X の mmap 問題に対する私の結論は、ファイル全体をマップし、マップしたままにすることです。ファイルを拡張する必要がある場合は、必要以上に多くのバイトをマップして、たまに再マップするだけで済むようにします。

これを機能させるには、64 ビット OS X を使用する必要がある場合があります。

于 2011-04-28T16:41:28.980 に答える