开放多个档案的中央战略

时间:2010-12-26 02:23:53

标签: objective-c c macos optimization grand-central-dispatch

我有一个使用Grand Central调度队列的工作实现:(1)打开文件并在“queue1”上计算OpenSSL DSA哈希,(2)将哈希写出到新的“侧车”文件以便以后验证“队列2”。

我想同时打开多个文件,但是基于一些不会通过打开100个文件并超过硬盘驱动器可持续输出而“阻塞”操作系统的逻辑。照片浏览应用程序(如iPhoto或Aperture)似乎打开多个文件并显示它们,所以我假设可以这样做。

我假设最大的限制是磁盘I / O,因为应用程序可以(理论上)同时读写多个文件。

有什么建议吗?

TIA

5 个答案:

答案 0 :(得分:7)

你是正确的,因为你肯定会受到I / O限制。而且,随着多个文件打开并同时被主动阅读的随机访问性质,它将更加复杂。

因此,你需要取得一些平衡。正如您所观察到的那样,更有可能的是,一个文件并不是最有效的。

个人?

我会使用调度信号量。

类似的东西:

@property(nonatomic, assign) dispatch_queue_t dataQueue;
@property(nonatomic, assign) dispatch_semaphore_t execSemaphore;

- (void) process:(NSData *)d {
    dispatch_async(self.dataQueue, ^{
        if (!dispatch_semaphore_wait(self.execSemaphore, DISPATCH_TIME_FOREVER)) {
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                ... do calcualtion work here on d ...
                dispatch_async(dispatch_get_main_queue(), ^{
                    .... update main thread w/new data here ....
                });
                dispatch_semaphore_signal(self.execSemaphore);
            });
        }
    });
}

它开始于:

self.dataQueue = dispatch_queue_create("com.yourcompany.dataqueue", NULL);
self.execSemaphore = dispatch_semaphore_create(3);
[self process: ...];
[self process: ...];
[self process: ...];
[self process: ...];
[self process: ...];
.... etc ....

您需要确定如何最好地处理排队。如果有很多项目并且有取消的概念,那么将所有内容排入队列可能会造成浪费。同样,您可能希望将URL排入要处理的文件,而不是像上面那样的NSData对象。

无论如何,上述情况将同时处理三件事,无论有多少人入队。

答案 1 :(得分:6)

我使用NSOperation是因为它易于处理依赖关系和取消。

我创建了一个操作,分别用于读取数据文件,计算数据文件的哈希值以及编写sidecar文件。我将使每个写操作依赖于其相关的计算操作,并且每个计算操作都依赖于其相关的读操作。

然后我将读取和写入操作添加到一个NSOperationQueue,即“I / O队列”,其宽度有限。我将计算操作添加到一个单独的NSOperationQueue,即“计算队列”,其宽度不受限制。

I / O队列宽度受限的原因是您的工作可能受I / O限制;您可能希望它的宽度大于1,但很可能与输入文件所在的物理磁盘数量直接相关。 (可能类似于2x,你需要通过实验来确定。)

代码最终会看起来像这样:

@implementation FileProcessor

static NSOperationQueue *FileProcessorIOQueue = nil;
static NSOperationQueue *FileProcessorComputeQueue = nil;

+ (void)inititalize
{
    if (self == [FileProcessor class]) {
        FileProcessorIOQueue = [[NSOperationQueue alloc] init];
        [FileProcessorIOQueue setName:@"FileProcessorIOQueue"];
        [FileProcessorIOQueue setMaxConcurrentOperationCount:2]; // limit width

        FileProcessorComputeQueue = [[NSOperationQueue alloc] init];
        [FileProcessorComputeQueue setName:@"FileProcessorComputeQueue"];
    }
}

- (void)processFilesAtURLs:(NSArray *)URLs
{
    for (NSURL *URL in URLs) {
        __block NSData *fileData = nil; // set by readOperation
        __block NSData *fileHashData = nil; // set by computeOperation

        // Create operations to do the work for this URL

        NSBlockOperation *readOperation =
            [NSBlockOperation blockOperationWithBlock:^{
                fileData = CreateDataFromFileAtURL(URL);
            }];

        NSBlockOperation *computeOperation =
            [NSBlockOperation blockOperationWithBlock:^{
                fileHashData = CreateHashFromData(fileData);
                [fileData release]; // created in readOperation
            }];

        NSBlockOperation *writeOperation =
            [NSBlockOperation blockOperationWithBlock:^{
                WriteHashSidecarForFileAtURL(fileHashData, URL);
                [fileHashData release]; // created in computeOperation
            }];

        // Set up dependencies between operations

        [computeOperation addDependency:readOperation];
        [writeOperation addDependency:computeOperation];

        // Add operations to appropriate queues

        [FileProcessorIOQueue addOperation:readOperation];
        [FileProcessorComputeQueue addOperation:computeOperation];
        [FileProcessorIOQueue addOperation:writeOperation];
    }
}

@end

这很简单;而不是处理与dispatch_* API一样的多重嵌套的同步/异步层,NSOperation允许您独立定义工作单元和它们之间的依赖关系。在某些情况下,这可以更容易理解和调试。

答案 2 :(得分:6)

你已经收到了很好的答案,但我想补充几点。我曾参与枚举文件系统中所有文件的项目,并计算每个文件的MD5和SHA1哈希值(除了其他处理)。如果您正在做类似的事情,在那里搜索大量文件并且文件可能包含任意内容,那么需要考虑以下几点:

  • 如上所述,您将受到I / O限制。如果同时读取多个文件,则会对每个计算的性能产生负面影响。显然,并行调度计算的目标是使磁盘在文件之间保持忙碌,但您可能需要考虑以不同方式构建工作。例如,设置一个枚举并打开文件的线程,第二个线程从第一个线程一次获取打开文件句柄并处理它们。文件系统将缓存目录信息,因此枚举不会对读取数据产生严重影响,这实际上必须击中磁盘。

  • 如果文件可能是任意大的,那么Chris的方法可能不实用,因为整个内容都被读入内存。

  • 如果您对数据没有其他用途而不是计算哈希值,那么我建议在读取数据之前禁用文件系统缓存。

如果使用NSFileHandles,一个简单的类别方法将按文件执行此操作:

@interface NSFileHandle (NSFileHandleCaching)
- (BOOL)disableFileSystemCache;
@end

#include <fcntl.h>

@implementation NSFileHandle (NSFileHandleCaching)
- (BOOL)disableFileSystemCache {
     return (fcntl([self fileDescriptor], F_NOCACHE, 1) != -1);
}
@end
  • 如果边车文件很小,您可能希望将它们收集在内存中并分批写出来,以尽量减少处理中断。

  • 文件系统(至少HFS)按顺序存储文件在目录中的文件记录,因此遍历文件系统广度优先(即,在进入子目录之前处理目录中的每个文件)。

以上只是建议,当然。您需要尝试并测量性能以确认实际影响。

答案 3 :(得分:2)

libdispatch实际上为此明确提供了API!查看dispatch_io;它将在适当的时候处理并行化IO,否则将其序列化以避免颠簸磁盘。

答案 4 :(得分:1)

以下链接指向我使用NSOperation和Grand Central Dispatch设置的BitBucket项目,使用原始文件完整性应用程序。

https://bitbucket.org/torresj/hashar-cocoa

我希望它有所帮助/使用。