Есть ли способ ускорить этот код ввода/вывода файла?
В моем приложении у меня около 300 NSData
объектов размером 0,5 МБ, и я записываю их последовательно в файл по существу с этим кодом (который записывает один объект размером 0,5 МБ 300 раз):
- (void)createFile {
// create .5 MB block to write
int size = 500000;
Byte *bytes = malloc(size);
for (int i = 0; i < size; i++) {
bytes[i] = 42;
}
NSData *data = [NSData dataWithBytesNoCopy:bytes length:size
freeWhenDone:YES];
// temp output file
NSUUID *uuid = [NSUUID UUID];
NSString *path = [[NSTemporaryDirectory()
stringByAppendingPathComponent:[uuid UUIDString]]
stringByAppendingPathExtension:@"dat"];
NSOutputStream *outputStream = [[NSOutputStream alloc]
initToFileAtPath:path append:NO];
[outputStream open];
double startTime = CACurrentMediaTime();
NSInteger totalBytesWritten;
NSInteger bytesWritten;
Byte *readPtr;
for (int i = 0; i < 300; i++) {
// reset read pointer to block we're writing to the output
readPtr = (Byte *)[data bytes];
totalBytesWritten = 0;
// write the block
while (totalBytesWritten < size) {
bytesWritten = [outputStream write:readPtr maxLength:size
- totalBytesWritten];
readPtr += bytesWritten;
totalBytesWritten += bytesWritten;
}
}
double duration = CACurrentMediaTime() - startTime;
NSLog(@"duration = %f", duration);
[outputStream close];
}
Как на моем iPod (5-го поколения), так и на моем iPhone 6, этот процесс занимает около 3 секунд, и мне было интересно, есть ли какой-нибудь более быстрый способ сделать это. Я пробовал использовать подходы NSFileManager
и NSFileHandle
, но они занимают примерно один и тот же отрезок времени, что приводит меня к предположению, что это фундаментальный предел ввода-вывода, в котором я работаю.
Есть ли способ сделать это быстрее (этот код должен компилироваться и запускаться на любом устройстве)?
Ответы
Ответ 1
Здесь самая высокая производительность, которую я смог достичь, используя mmap и memcpy.
Требуется в среднем около 0,2 секунды для запуска на моем iPhone 6 с некоторым изменением примерно до 0,5 с. YMMV, однако, как представляется, iPhone 6 имеет два разных поставщика флэш-памяти: один - это TLC, а другой - MLC - те, у кого есть TLC, получат значительно лучшие результаты.
Это, конечно, предполагает, что вы в порядке с асинхронным вводом-выводом. Если вам действительно нужно что-то синхронное, ищите другие решения.
- (IBAction)createFile {
NSData *data = [[self class] dataToCopy];
// temp output file
NSUUID *uuid = [NSUUID UUID];
NSString *path = [[NSTemporaryDirectory()
stringByAppendingPathComponent:[uuid UUIDString]]
stringByAppendingPathExtension:@"dat"];
NSUInteger size = [data length];
NSUInteger count = 300;
NSUInteger file_size = size * count;
int fd = open([path UTF8String], O_CREAT | O_RDWR, 0666);
ftruncate(fd, file_size);
void *addr = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
double startTime = CACurrentMediaTime();
static dispatch_queue_t concurrentDataQueue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
concurrentDataQueue = dispatch_queue_create("test.concurrent", DISPATCH_QUEUE_CONCURRENT);
});
for (int i = 0; i < count; i++) {
dispatch_async(concurrentDataQueue, ^{
memcpy(addr + (i * size), [data bytes], size);
});
}
dispatch_barrier_async(concurrentDataQueue, ^{
fsync(fd);
double duration = CACurrentMediaTime() - startTime;
NSLog(@"duration = %f", duration);
munmap(addr, file_size);
close(fd);
unlink([path UTF8String]);
});
}
Ответ 2
300 * 0,5/3 = 50 МБ/с для 150 МБ данных выглядит достаточно быстро. Полагаю, вы нажмете ограничение скорости WRITE для флэш-памяти. Я считаю, что вы запускаете этот код в фоновом потоке, поэтому проблема заключается не в блокировании пользовательского интерфейса
Ответ 3
Два рекомендации по производительности, которые я могу вам порекомендовать: попробуйте отключить кеширование файловой системы или проверить размер буфера ввода-вывода.
"При чтении данных, которые, как вы уверены, вам не понадобится в ближайшее время, например, потоковой передачи большого мультимедийного файла, сообщите файловой системе, чтобы они не добавляли эти данные в кэш файловой системы.
Приложения могут вызывать функцию BSD fcntl
с флагом F_NOCACHE
, чтобы включить или отключить кеширование для файла. Дополнительные сведения об этой функции см. В fcntl
. "~ Рекомендации по производительности
или "прочитать много или все данные в памяти перед его обработкой" ~ Рекомендации по производительности
iPhone 6 использует 16-гигабайтную флэш-память hynix для SK ~ [Teardown], а теоретический предел для последовательной записи составляет от 40 до 70 Мбит/с ~ [NAND flash].