我在一个非常大的文件中运行fseek(..)
的性能不佳。
每次调用fseek
函数时,我都需要向后移动文件指针位置100 bytes
:
fseek(fp, -100, SEEK_CUR);
之前,我这样做:
fseek(fp, (index)*100, SEEK_SET); // which makes basically the same...
我的问题是fseek如何将指针移动到文件中并将文件指针设置在特定位置。
我认为它需要文件指针并将其向后移动,但现在我认为它真正的作用是
获取当前位置(cp
)
添加否定索引(p = idx + cp
)
并将文件指针从文件的开头移动到该位置(fseek(fp, p, SEEK_SET)
)
答案 0 :(得分:3)
在用户应用程序级别,您会想到一个像大块内存的文件,并将文件指针移动为简单的内存操作(递增或递减指针以获得文件中所需的偏移量)。
但是在运行时库和操作系统级别,情况完全不同。处理文件的运行时库代码不会将文件的整个内容加载到内存中。也许文件非常大,也许你只需要从中读取几个字节,原因很多。
运行时库(以及由OS管理的文件缓存)仅从内存缓冲区中的文件加载一些数据。您使用该数据(读取,写入),当您想要访问缓冲区中尚未加载的信息时,文件管理代码会为您加载它;也许它会扩大缓冲区或者它可能会将缓冲区写入文件(如果它被修改),或者只是丢弃先前加载的数据(如果它没有被修改)并在缓冲区中加载另一块数据。
当您使用fseek()
跳转到文件的其他部分时,文件指针通常会到达尚未在内存中的区域。我想它从文件指针的新位置开始加载数据(在操作系统级别,文件缓存将数据加载到多个磁盘块中)。因为你在文件中向后运行,我想文件指针新位置的数据几乎从未在内存中加载过。它会触发磁盘访问,这会使它变慢。
我认为最适合您的解决方案是使用操作系统提供的功能将文件映射到内存中。在Linux上(或可能在OSX上)或在mmap()
上阅读File Mapping。它可以帮助您,但由于您使用的特定访问模式,改进可能并不重要。大多数情况下,程序从头到尾读取文件,处理文件和磁盘访问的代码针对此模式进行了优化。
答案 1 :(得分:3)
首先,您使用的操作系统是什么?如果是Linux,请在strace
下运行您的应用程序,看看系统是否正在调用它。
其次,fopen()/fseek()/fread()
是此访问模式的错误工具。这些调用缓冲文件读取 - 通过读取提前。那对你没有好处。你fseek()
偏移X,无论缓冲的数据现在都没用,你fread()
100个字节,缓冲的fread()
读取更多 - 可能是8 kB。您可能会阅读文件的几乎每个字节超过80次。您可以使用use setbuf()
或setvbuf()
来禁用缓冲,但是当您向后浏览文件时,您将执行100字节读取。它应该更快,但速度不会快。
尽可能快地执行此操作(无需进入多线程和/或异步IO):
使用open()/pread()
。您不需要寻求 - pread()
直接从任意偏移读取。
阅读更大的块 - 比如说8192 x 100.甚至更大。像以前一样向后读,但是自己做缓冲并从文件中的偏移量开始,该偏移是您正在读取的大尺寸的倍数 - 第一次读取可能少于819,200字节。首先处理缓冲区中的最后100个字节,然后通过缓冲区向后。当您处理缓冲区中的前100个字节时,请使用pread()
从文件中读取之前的819,200个字节(甚至更大)。
如果可用,请使用直接IO。文件系统优化可能会尝试优化"通过阅读提前并将数据放入页面缓存(您已经处理过的数据)进行访问。因此,如果可能,请绕过页面缓存(并非所有操作系统都支持直接IO,并且并非所有支持直接IO的操作系统上的文件系统都实现它。)
这样的事情:
#define DATA_SIZE 100
#define NUM_CHUNKS (32UL * 1024UL)
#define READ_SIZE ( ( size_t ) DATA_SIZE * NUM_CHUNKS )
void processBuffer( const char *buffer, ssize_t bytes )
{
if ( bytes <= 0 ) return;
// process a buffer backwards...
}
void processFile( const char *filename )
{
struct stat sb;
// get page-aligned buffer for direct IO
char *buffer = valloc( READ_SIZE );
// Linux-style direct IO
int fd = open( filename, O_RDONLY | O_DIRECT );
fstat( fd, &sb );
// how many read operations?
// use lldiv() to get quotient and remainder in one op
lldiv_t numReads = lldiv( sb.st_size, READ_SIZE );
if ( numReads.rem )
{
numReads.quot++;
}
while ( numReads.quot > 0 )
{
numReads.quot--;
ssize_t bytesRead = pread( fd, buffer,
READ_SIZE, numReads.quot * READ_SIZE );
processBuffer( buffer, bytesRead );
}
free( buffer );
close( fd );
}
您需要为此添加错误处理。