Я нашел другой способ записи данных, который быстрее обычной функции 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)
'write' работает медленнее, так как работает с OS и libc I/O буферизацией, а mmap переходит на диск почти напрямую. Дело в том, сколько накладных расходов на создание/усечение файла/etc. больше, чем эта буферизация ввода-вывода 'write'. В зависимости от этого вы можете выиграть или проиграть. – GMichael
@GMichael write - это чистая ОС, а не буферизация libc, только буферизация ОС. –
@ Jean-BaptisteYunès Это зависит от реализации. Даже если вы правы, все еще есть буферизация ОС. – GMichael