在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");
}
具体来说,比较两台机器
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
即使考虑到内存量的减少,考虑到文件只是物理内存的一半,这似乎是不寻常的。任何人都可以指出代码更改或配置更改可能会提高性能吗?
我们尝试使用read而不是mmaps,它确实产生了很大的不同,但这样做需要对现有代码库进行大量更改(并且mmap比linux上的读取快得多)。
答案 0 :(得分:4)
我认为你没有衡量正确的事情。我检查了测试的内部部分,我的gcc版本能够完全优化循环。
当我将mblock
指针声明为指向volatile
数据的指针时,这会发生变化。然后编译器必须对循环中的语句执行所有副作用,特别是从内存中对其进行充电。
因此,您可以从测试中得出的唯一结论是:
因此,如果您可以重新测试您的测试,我会感兴趣的是看到两个系统在该功能方面的真正差异。
答案 1 :(得分:3)
在以下情况下不应使用文件映射:
您想要按顺序读取文件 从头到尾只有一次。
该文件是几百兆字节 或更大的尺寸。 (映射大文件 快速填充虚拟内存空间。 此外,您的计划可能不会 如果有的话,有可用的空间 已经运行了一段时间或它 内存空间是碎片化的。)
对于大型顺序读取操作,最好禁用磁盘缓存并将文件读入小内存缓冲区。有关详细信息,请参阅“有选择地缓存文件”。
以下是演示此问题的代码段:
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 );
不知道他们是否以及何时会修复它。
答案 2 :(得分:1)
我对OS X mmap问题的结论是映射整个文件并保持映射。如果您需要扩展文件,请映射多于您需要的字节数,以便您只需偶尔重新映射。
您可能需要使用64位OS X来完成这项工作。