如何优化c++二进制文件读取?
How to optimize c++ binary file reading?
我有一个复杂的解释器从(有时)多个文件中读取命令(确切的细节超出范围)但它需要多次迭代这些多个文件(有些可能是 GB 大小,防止很好的缓冲) .
我希望提高从文件中读取每个命令的速度。
我已使用 RDTSC(程序计数器)寄存器对代码进行微基准测试,足以了解 >80% 的时间花在了从文件中读取。
事情是这样的:生成输入文件的程序实际上比在我的小型解释器中读入文件要快。也就是说,我可以(理论上)不输出文件,而是 link 将数据生成器发送给解释器并跳过文件,但这应该不会更快,对吧?
我做错了什么?或者写入应该比从文件中读取快 2 到 3 倍(至少)?
我考虑过 mmap,但 http://lemire.me/blog/archives/2012/06/26/which-is-fastest-read-fread-ifstream-or-mmap/ 上的一些结果似乎表明它并不比 ifstream 快。还是 mmap 在这种情况下有帮助?
详情:
我(到目前为止)已经尝试添加缓冲区、调整参数、删除 ifstream 缓冲区(在我的测试用例中将其减慢了 6 倍),我目前在四处搜索后不知所措。
代码的重要部分如下。它执行以下操作:
- 如果数据留在缓冲区中,将表单缓冲区复制到 memblock(然后使用它)
- 如果缓冲区中没有数据,请检查文件中还剩多少数据,如果超过缓冲区大小,则复制一个缓冲区大小的块
如果小于文件
//if data in buffer
if(leftInBuffer[activefile] > 0)
{
//cout <<bufferloc[activefile] <<"\n";
memcpy(memblock,(buffer[activefile])+bufferloc[activefile],16);
bufferloc[activefile]+=16;
leftInBuffer[activefile]-=16;
}
else //buffers blank
{
//read in block
long blockleft = (cfilemax -cfileplace) / 16 ;
int read=0;
/* slow block starts here */
if(blockleft >= MAXBUFELEMENTS)
{
currentFile->read((char *)(&(buffer[activefile][0])),16*MAXBUFELEMENTS);
leftInBuffer[activefile] = 16*MAXBUFELEMENTS;
bufferloc[activefile]=0;
read =16*MAXBUFELEMENTS;
}
else //read in part of the block
{
currentFile->read((char *)(&(buffer[activefile][0])),16*(blockleft));
leftInBuffer[activefile] = 16*blockleft;
bufferloc[activefile]=0;
read =16*blockleft;
}
/* slow block ends here */
memcpy(memblock,(buffer[activefile])+bufferloc[activefile],16);
bufferloc[activefile]+=16;
leftInBuffer[activefile]-=16;
}
编辑:这是在 mac、osx 10.9.5 上,i7 和 SSD
解决方案:
正如下面所建议的,mmap 能够将速度提高大约 10 倍。
(对于搜索此内容的任何其他人)
具体打开方式:
uint8_t * openMMap(string name, long & size)
{
int m_fd;
struct stat statbuf;
uint8_t * m_ptr_begin;
if ((m_fd = open(name.c_str(), O_RDONLY)) < 0)
{
perror("can't open file for reading");
}
if (fstat(m_fd, &statbuf) < 0)
{
perror("fstat in openMMap failed");
}
if ((m_ptr_begin = (uint8_t *)mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, m_fd, 0)) == MAP_FAILED)
{
perror("mmap in openMMap failed");
}
uint8_t * m_ptr = m_ptr_begin;
size = statbuf.st_size;
return m_ptr;
}
阅读者:
uint8_t * mmfile = openMMap("my_file", length);
uint32_t * memblockmm;
memblockmm = (uint32_t *)mmfile; //cast file to uint32 array
uint32_t data = memblockmm[0]; //take int
mmfile +=4; //increment by 4 as I read a 32 bit entry and each entry in mmfile is 8 bits.
这应该是一个评论,但我没有 50 的声誉来发表评论。
MAXBUFELEMENTS 的值是多少?根据我的经验,许多较小的读取比一次较大的读取要慢得多。我建议尽可能读取整个文件,有些文件可能是 GB,但即使一次读取 100MB 也比读取 1MB 100 次性能更好。
如果这仍然不够好,接下来您可以尝试压缩 (zlib) 输入文件(由于大小可能必须将它们分成块),然后在内存中解压缩它们。这种方法通常比读取未压缩文件更快。
作为 ,尝试调整缓冲区大小以查看是否有帮助。
尝试 mmap 看看是否有帮助。
我假设 currentFile
是 std::ifstream
?使用 iostreams
会有一些开销(例如,istream
将进行自己的缓冲,为您正在做的事情添加一个额外的层);虽然我不希望开销很大,但您可以直接使用 open(2) and read(2) 进行测试。
您应该能够通过 dtruss -e
运行 您的代码来验证 read
系统调用需要多长时间。如果这些占用了您的大部分时间,那么您将达到 OS 和硬件限制,因此您可以通过管道、mmap'ing 或调整缓冲区大小来解决这个问题。如果这些花费的时间比您预期的少,那么请在您的应用程序逻辑中查找问题(每次迭代中的不必要工作等)。
我有一个复杂的解释器从(有时)多个文件中读取命令(确切的细节超出范围)但它需要多次迭代这些多个文件(有些可能是 GB 大小,防止很好的缓冲) .
我希望提高从文件中读取每个命令的速度。
我已使用 RDTSC(程序计数器)寄存器对代码进行微基准测试,足以了解 >80% 的时间花在了从文件中读取。
事情是这样的:生成输入文件的程序实际上比在我的小型解释器中读入文件要快。也就是说,我可以(理论上)不输出文件,而是 link 将数据生成器发送给解释器并跳过文件,但这应该不会更快,对吧?
我做错了什么?或者写入应该比从文件中读取快 2 到 3 倍(至少)?
我考虑过 mmap,但 http://lemire.me/blog/archives/2012/06/26/which-is-fastest-read-fread-ifstream-or-mmap/ 上的一些结果似乎表明它并不比 ifstream 快。还是 mmap 在这种情况下有帮助?
详情:
我(到目前为止)已经尝试添加缓冲区、调整参数、删除 ifstream 缓冲区(在我的测试用例中将其减慢了 6 倍),我目前在四处搜索后不知所措。
代码的重要部分如下。它执行以下操作:
- 如果数据留在缓冲区中,将表单缓冲区复制到 memblock(然后使用它)
- 如果缓冲区中没有数据,请检查文件中还剩多少数据,如果超过缓冲区大小,则复制一个缓冲区大小的块
如果小于文件
//if data in buffer if(leftInBuffer[activefile] > 0) { //cout <<bufferloc[activefile] <<"\n"; memcpy(memblock,(buffer[activefile])+bufferloc[activefile],16); bufferloc[activefile]+=16; leftInBuffer[activefile]-=16; } else //buffers blank { //read in block long blockleft = (cfilemax -cfileplace) / 16 ; int read=0; /* slow block starts here */ if(blockleft >= MAXBUFELEMENTS) { currentFile->read((char *)(&(buffer[activefile][0])),16*MAXBUFELEMENTS); leftInBuffer[activefile] = 16*MAXBUFELEMENTS; bufferloc[activefile]=0; read =16*MAXBUFELEMENTS; } else //read in part of the block { currentFile->read((char *)(&(buffer[activefile][0])),16*(blockleft)); leftInBuffer[activefile] = 16*blockleft; bufferloc[activefile]=0; read =16*blockleft; } /* slow block ends here */ memcpy(memblock,(buffer[activefile])+bufferloc[activefile],16); bufferloc[activefile]+=16; leftInBuffer[activefile]-=16; }
编辑:这是在 mac、osx 10.9.5 上,i7 和 SSD
解决方案:
正如下面所建议的,mmap 能够将速度提高大约 10 倍。
(对于搜索此内容的任何其他人) 具体打开方式:
uint8_t * openMMap(string name, long & size)
{
int m_fd;
struct stat statbuf;
uint8_t * m_ptr_begin;
if ((m_fd = open(name.c_str(), O_RDONLY)) < 0)
{
perror("can't open file for reading");
}
if (fstat(m_fd, &statbuf) < 0)
{
perror("fstat in openMMap failed");
}
if ((m_ptr_begin = (uint8_t *)mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, m_fd, 0)) == MAP_FAILED)
{
perror("mmap in openMMap failed");
}
uint8_t * m_ptr = m_ptr_begin;
size = statbuf.st_size;
return m_ptr;
}
阅读者:
uint8_t * mmfile = openMMap("my_file", length);
uint32_t * memblockmm;
memblockmm = (uint32_t *)mmfile; //cast file to uint32 array
uint32_t data = memblockmm[0]; //take int
mmfile +=4; //increment by 4 as I read a 32 bit entry and each entry in mmfile is 8 bits.
这应该是一个评论,但我没有 50 的声誉来发表评论。
MAXBUFELEMENTS 的值是多少?根据我的经验,许多较小的读取比一次较大的读取要慢得多。我建议尽可能读取整个文件,有些文件可能是 GB,但即使一次读取 100MB 也比读取 1MB 100 次性能更好。
如果这仍然不够好,接下来您可以尝试压缩 (zlib) 输入文件(由于大小可能必须将它们分成块),然后在内存中解压缩它们。这种方法通常比读取未压缩文件更快。
作为
尝试 mmap 看看是否有帮助。
我假设 currentFile
是 std::ifstream
?使用 iostreams
会有一些开销(例如,istream
将进行自己的缓冲,为您正在做的事情添加一个额外的层);虽然我不希望开销很大,但您可以直接使用 open(2) and read(2) 进行测试。
您应该能够通过 dtruss -e
运行 您的代码来验证 read
系统调用需要多长时间。如果这些占用了您的大部分时间,那么您将达到 OS 和硬件限制,因此您可以通过管道、mmap'ing 或调整缓冲区大小来解决这个问题。如果这些花费的时间比您预期的少,那么请在您的应用程序逻辑中查找问题(每次迭代中的不必要工作等)。