我的Mac上fseek()
/的写入性能有问题。fwrite()
我正在处理最大 4 GB 的大文件,下面的测试是用一个只有 120 MB 的相当小的文件进行的。我的策略如下:
fopen()
磁盘上的新文件- 用零填充文件(大约需要 3 秒)
- 将小块数据写入随机位置(30.000 个块,每个 4k)
整个过程大约需要 120 秒。
写入策略绑定到图像旋转算法(请参阅我的问题here),除非有人为旋转问题提出更快的解决方案,否则我无法更改使用fseek()
然后将 4k 或更少写入文件的策略.
我观察到的是:前几千个fseek()
/fwrite()
性能相当不错,但性能下降得非常快,比任何系统缓存被填满时所期望的要快。下图显示了fwrite()
每秒 s 与以秒为单位的时间。如您所见,7 秒后fseek()
/fwrite()
速率达到约。每秒 200 次,仍在下降,直到在过程结束时达到每秒 100 次。
在该过程的中间(2 或 3 次),操作系统决定将文件内容刷新到磁盘,我可以从控制台输出中看到它挂了几秒钟,在此期间我大约有。在我的磁盘上写入 5 MB/s(这不算多)。在fclose()
系统似乎写入整个文件后,我看到 20 MB/s 的磁盘活动持续了更长的时间。
如果我fflush()
每 5.000fwrite()
秒使用一次,则行为根本不会改变。放入fclose()
/fopen()
强制冲洗以某种方式加速了整个事情。10%。
我确实描述了这个过程(下面的截图),你看,几乎所有的时间都花在了里面fwrite()
,fseek()
而且可以深入到__write_nocancel()
他们两个人身上。
完全荒谬的总结
想象一下我的输入数据完全适合我的缓冲区的情况,因此我能够线性写入我的旋转输出数据,而无需将写入过程分成片段。我仍然使用fseek()
定位文件指针,只是因为写入函数的逻辑是这样的,但在这种情况下,文件指针被设置到它已经存在的相同位置。人们预计不会对性能产生影响。错了。
荒谬的是,如果我删除fseek()
对该特殊情况的调用,我的函数会在2.7 秒内完成,而不是 120 秒。
fseek()
现在,经过很长的前言,问题是:即使我寻求相同的位置,为什么会对性能产生如此大的影响?我怎样才能加快速度(通过其他策略或其他函数调用,如果可能,禁用缓存,内存映射访问,......)?
作为参考,这是我的代码(未整理,未优化,包含大量调试输出):
-(bool)writeRotatedRaw:(TIFF*)tiff toFile:(NSString*)strFile
{
if(!tiff) return NO;
if(!strFile) return NO;
NSLog(@"Starting to rotate '%@'...", strFile);
FILE *f = fopen([strFile UTF8String], "w");
if(!f)
{
NSString *msg = [NSString stringWithFormat:@"Could not open '%@' for writing.", strFile];
NSRunAlertPanel(@"Error", msg, @"OK", nil, nil);
return NO;
}
#define LINE_CACHE_SIZE (1024*1024*256)
int h = [tiff iImageHeight];
int w = [tiff iImageWidth];
int iWordSize = [tiff iBitsPerSample]/8;
int iBitsPerPixel = [tiff iBitsPerSample];
int iLineSize = w*iWordSize;
int iLinesInCache = LINE_CACHE_SIZE / iLineSize;
int iLinesToGo = h, iLinesToRead;
NSLog(@"Creating temporary file");
double time = CACurrentMediaTime();
double lastTime = time;
unsigned char *dummy = calloc(iLineSize, 1);
for(int i=0; i<h; i++) fwrite(dummy, 1, iLineSize, f);
free(dummy);
fclose(f);
f = fopen([strFile UTF8String], "w");
NSLog(@"Created temporary file (%.1f MB) in %.1f seconds", (float)iLineSize*(float)h/1024.0f/1024.0f, CACurrentMediaTime()-time);
fseek(f, 0, SEEK_SET);
lastTime = CACurrentMediaTime();
time = CACurrentMediaTime();
int y=0;
unsigned char *ucRotatedPixels = malloc(iLinesInCache*iWordSize);
unsigned short int *uRotatedPixels = (unsigned short int*)ucRotatedPixels;
unsigned char *ucLineCache = malloc(w*iWordSize*iLinesInCache);
unsigned short int *uLineCache = (unsigned short int*)ucLineCache;
unsigned char *uc;
unsigned int uSizeCounter=0, uMaxSize = iLineSize*h, numfwrites=0, lastwrites=0;
while(iLinesToGo>0)
{
iLinesToRead = iLinesToGo;
if(iLinesToRead>iLinesInCache) iLinesToRead = iLinesInCache;
for(int i=0; i<iLinesToRead; i++)
{
// read as much lines as fit into buffer
uc = [tiff getRawLine:y+i withBitsPerPixel:iBitsPerPixel];
memcpy(ucLineCache+i*iLineSize, uc, iLineSize);
}
for(int x=0; x<w; x++)
{
if(iBitsPerPixel==8)
{
for(int i=0; i<iLinesToRead; i++)
{
ucRotatedPixels[iLinesToRead-i-1] = ucLineCache[i*w+x];
}
fseek(f, w*x+(h-y-1), SEEK_SET);
fwrite(ucRotatedPixels, 1, iLinesToRead, f);
numfwrites++;
uSizeCounter += iLinesToRead;
if(CACurrentMediaTime()-lastTime>1.0)
{
lastTime = CACurrentMediaTime();
NSLog(@"Progress: %.1f %%, x=%d, y=%d, iLinesToRead=%d\t%d", (float)uSizeCounter * 100.0f / (float)uMaxSize, x, y, iLinesToRead, numfwrites);
}
}
else
{
for(int i=0; i<iLinesToRead; i++)
{
uRotatedPixels[iLinesToRead-i-1] = uLineCache[i*w+x];
}
fseek(f, (w*x+(h-y-1))*2, SEEK_SET);
fwrite(uRotatedPixels, 2, iLinesToRead, f);
uSizeCounter += iLinesToRead*2;
if(CACurrentMediaTime()-lastTime>1.0)
{
lastTime = CACurrentMediaTime();
NSLog(@"Progress: %.1f %%, x=%d, y=%d, iLinesToRead=%d\t%d", (float)uSizeCounter * 100.0f / (float)uMaxSize, x, y, iLinesToRead, numfwrites);
}
}
}
y += iLinesInCache;
iLinesToGo -= iLinesToRead;
}
free(ucLineCache);
free(ucRotatedPixels);
fclose(f);
NSLog(@"Finished, %.1f s", (CACurrentMediaTime()-time));
return YES;
}
我有点迷茫,因为我不明白系统如何“优化”我的通话。任何输入表示赞赏。