有没有办法加速这个文件I / O代码?

时间:2015-06-06 03:22:17

标签: ios objective-c

在我的应用程序中,我有大约300个NSData个大小为0.5 MB的对象,我将它们全部按顺序编写到一个文件中,基本上是这个代码(写入一个0.5 MB的对象300次): / p>

- (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秒钟,我想知道是否有更快的方法可以做到这一点。我尝试过使用NSFileManagerNSFileHandle方法,但它们花费的时间大致相同,这使我认为这是我遇到的基本I / O限制。

有没有办法更快地执行此操作(此代码应在任何设备上编译和运行)?

3 个答案:

答案 0 :(得分:4)

这是我能够实现的最高性能,使用mmap&amp;的memcpy。

我的iPhone 6平均需要0.2秒才能运行,有些变化可达0.5秒左右。然而,YMMV看起来iPhone 6有两个不同的闪存存储提供商 - 一个是TLC,另一个是MLC--那些使用TLC的人将获得更好的结果。

这当然假设您可以使用异步I / O.如果您确实需要同步的东西,请寻找其他解决方案。

- (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]);
  });
}

答案 1 :(得分:2)

300 * 0.5 / 3 = 50MB / s,150MB数据看起来足够快。我想你已达到闪存存储WRITE速度限制。我相信您在后台线程中运行此代码,因此问题不在于阻止UI

答案 2 :(得分:1)

我建议的两个性能提示是:尝试关闭文件系统缓存或检查IO缓冲区大小。

“在读取您确定不再需要的数据时,例如流式传输大型多媒体文件,请告诉文件系统不要将该数据添加到文件系统缓存中。

应用可以使用fcntl标志调用BSD F_NOCACHE函数来启用或禁用文件的缓存。有关此功能的详细信息,请参阅fcntl。“〜Performance Tips

或“在处理之前将大部分或全部数据读入内存”〜Performance Tips

iPhone 6使用16Gb SK海力士闪存〜[Teardown],顺序写入的理论极限约为40至70 Mb / s~ [NAND flash]