2016-07-05 6 views
1

Я нашел другой способ записи данных, который быстрее обычной функции unix write.Почему (ftruncate + mmap + memcpy) быстрее, чем (писать)?

Во-первых, ftruncate файл на нужную нам длину, затем mmap этот блок файла, наконец, используя memcpy, чтобы очистить содержимое файла. Я приведу пример кода ниже.

Как известно, mmap может загружать файл в адресное пространство процесса, ускоряя его, игнорируя кеш страниц. НО, я не знаю, почему это может ускорить скорость записи.

Я пишу неправильный чехол или это может быть своего рода opti трюк?

Вот тестовый код. Некоторые из них написаны в ObjC, но неважно. WCTTicker - это только класс статистики, используя gettimeofday.

//find a dir to test 
NSString* document = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; 
NSString* dir = [document stringByAppendingPathComponent:@"testDir"]; 

//remove all existing test 
if ([[NSFileManager defaultManager] fileExistsAtPath:dir]) { 
    if (![[NSFileManager defaultManager] removeItemAtPath:dir error:nil]) { 
     NSLog(@"fail to remove dir"); 
     return; 
    } 
} 
//create dir to test 
if (![[NSFileManager defaultManager] createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:nil]) { 
    NSLog(@"fail to create dir"); 
} 

//pre-alloc memory 
const int length = 10000000; 
const int count = 100; 
char* mem = (char*)malloc(length); 
memset(mem, 'T', length); 

{ 
    //start testing mmap 
    // ftruncate && mmap(private) &&memcpy 
    NSString* mmapFileFormat = [dir stringByAppendingPathComponent:@"privateMmapFile%d"]; 
    [WCTTicker tick]; 
    for (int i = 0; i < count; i++) { 
     NSString* path = [[NSString alloc] initWithFormat:mmapFileFormat, i]; 
     int fd = open(path.UTF8String, O_CREAT | O_RDWR, S_IRWXG | S_IRWXU | S_IRWXO); 
     if (fd<0) { 
      NSLog(@"fail to open"); 
     } 
     int rc = ftruncate(fd, length); 
     if (rc<0) { 
      NSLog(@"fail to truncate"); 
     } 
     char* map = (char*)mmap(NULL, length, PROT_WRITE | PROT_READ, MAP_PRIVATE, fd, 0); 
     if (!map) { 
      NSLog(@"fail to mmap"); 
     } 
     memcpy(map, mem, length); 
     close(fd); 
    } 
    [WCTTicker stop]; 
} 

{ 
    //start testing write 
    // normal write 
    NSString* writeFileFormat = [dir stringByAppendingPathComponent:@"writeFile%d"]; 
    [WCTTicker tick]; 
    for (int i = 0; i < count; i++) { 
     NSString* path = [[NSString alloc] initWithFormat:writeFileFormat, i]; 
     int fd = open(path.UTF8String, O_CREAT | O_RDWR, S_IRWXG | S_IRWXU | S_IRWXO); 
     if (fd<0) { 
      NSLog(@"fail to open"); 
     } 
     int written = (int)write(fd, mem, length); 
     if (written!=length) { 
      NSLog(@"fail to write"); 
     } 
     close(fd); 
    } 
    [WCTTicker stop]; 
} 

{ 
    //start testing mmap 
    // ftruncate && mmap(shared) &&memcpy 
    NSString* mmapFileFormat = [dir stringByAppendingPathComponent:@"sharedMmapFile%d"]; 
    [WCTTicker tick]; 
    for (int i = 0; i < count; i++) { 
     NSString* path = [[NSString alloc] initWithFormat:mmapFileFormat, i]; 
     int fd = open(path.UTF8String, O_CREAT | O_RDWR, S_IRWXG | S_IRWXU | S_IRWXO); 
     if (fd<0) { 
      NSLog(@"fail to open"); 
     } 
     int rc = ftruncate(fd, length); 
     if (rc<0) { 
      NSLog(@"fail to truncate"); 
     } 
     char* map = (char*)mmap(NULL, length, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); 
     if (!map) { 
      NSLog(@"fail to mmap"); 
     } 
     memcpy(map, mem, length); 
     close(fd); 
    } 
    [WCTTicker stop]; 
} 

Вот результат теста:

2016-07-05 11:44:08.425 TestCaseiOS[4092:1070240] 
0: 1467690246.689788, info: (null) 
1: 1467690248.419790, cost 1.730002, info: (null) 
2016-07-05 11:44:14.126 TestCaseiOS[4092:1070240] 
0: 1467690248.427097, info: (null) 
1: 1467690254.126590, cost 5.699493, info: (null) 
2016-07-05 11:44:14.814 TestCaseiOS[4092:1070240] 
0: 1467690254.126812, info: (null) 
1: 1467690254.813698, cost 0.686886, info: (null) 
+0

'write' работает медленнее, так как работает с OS и libc I/O буферизацией, а mmap переходит на диск почти напрямую. Дело в том, сколько накладных расходов на создание/усечение файла/etc. больше, чем эта буферизация ввода-вывода 'write'. В зависимости от этого вы можете выиграть или проиграть. – GMichael

+0

@GMichael write - это чистая ОС, а не буферизация libc, только буферизация ОС. –

+0

@ Jean-BaptisteYunès Это зависит от реализации. Даже если вы правы, все еще есть буферизация ОС. – GMichael

ответ

1

Вы mmap() без соответствующего munmap()

С mmap страницы вручную (Linux)

MAP_SHARED Рассказать об этом отображении. Обновления сопоставления видны другим процессам, которые отображают этот файл и переносятся в базовый файл . Файл не может быть обновлен до тех пор, пока не будет вызван msync (2) или munmap().

Может быть, вы должны запустить тесты снова, так что есть вызов munmap:

char* map = (char*)mmap(NULL, length, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); 
if (!map) { 
    NSLog(@"fail to mmap"); 
} 
memcpy(map, mem, length); 
munmap(map, length); 
close(fd); 
+1

Точно. OP не измеряет ничего, кроме 'memcpy()'. 'mmap()' не обходит кеш страниц. Прочитайте, что [Linux Торвальдс должен сказать о 'mmap()'.] (Http://marc.info/?l=linux-kernel&m=95496636207616&w=2) –

0

Даже с munmap (или msync) добавил, я думаю, что это должно быть быстрее, по крайней мере для больших данных потому что write() приводит к операции копирования, а mmap и доступ к карте - нет.