非常快的文本文件处理 (C++)
very fast text file processing (C++)
我编写了一个在GPU上处理数据的应用程序。代码运行良好,但我有一个问题,即输入文件的读取部分(~3GB,文本(是我的应用程序的瓶颈。(从硬盘读取速度很快,但逐行处理速度较慢(。
我用 getline(( 读取一行并将第 1 行复制到向量,将第 2 行复制到向量并跳过第 3 行和第 4 行。其余 11 条 mio 线以此类推。
我尝试了几种方法在最佳时间获取文件:
我发现的最快的方法是使用boost::iostreams::stream
其他是:
- 将文件读取为 gzip 格式,以最小化 IO,但比直接读取慢阅读它。
- 通过读取将文件复制到RAM(文件指针,字符,长度(并用循环处理它以区分线条(也比 boost 慢(
有什么建议如何让它运行得更快吗?
void readfastq(char *filename, int SRlength, uint32_t blocksize){
_filelength = 0; //total datasets (each 4 lines)
_SRlength = SRlength; //length of the 2. line
_blocksize = blocksize;
boost::iostreams::stream<boost::iostreams::file_source>ins(filename);
in = ins;
readNextBlock();
}
void readNextBlock() {
timeval start, end;
gettimeofday(&start, 0);
string name;
string seqtemp;
string garbage;
string phredtemp;
_seqs.empty();
_phred.empty();
_names.empty();
_filelength = 0;
//read only a part of the file i.e the first 4mio lines
while (std::getline(in, name) && _filelength<_blocksize) {
std::getline(in, seqtemp);
std::getline(in, garbage);
std::getline(in, phredtemp);
if (seqtemp.size() != _SRlength) {
if (seqtemp.size() != 0)
printf("Error on read in fastq: size is invalidn");
} else {
_names.push_back(name);
for (int k = 0; k < _SRlength; k++) {
//handle special letters
if(seqtemp[k]== 'A') ...
else{
_seqs.push_back(5);
}
}
_filelength++;
}
}
编辑:
源文件可在 https://docs.google.com/open?id=0B5bvyb427McSMjM2YWQwM2YtZGU2Mi00OGVmLThkODAtYzJhODIzYjNhYTY2 下下载
由于一些指针问题,我更改了函数 readfastq 以读取文件。因此,如果您调用readfastq
,则blocksize
(以行为单位(必须大于要读取的行数。
溶液:
我找到了一个解决方案,它将文件的读取时间从 60 秒增加到 16 秒。我删除了处理特殊字符的内部循环并在 GPU 中执行此操作。这减少了读入时间,并且仅增加了最小的 GPU 运行时间。
感谢您的建议。
void readfastq(char *filename, int SRlength) {
_filelength = 0;
_SRlength = SRlength;
size_t bytes_read, bytes_expected;
FILE *fp;
fp = fopen(filename, "r");
fseek(fp, 0L, SEEK_END); //go to the end of file
bytes_expected = ftell(fp); //get filesize
fseek(fp, 0L, SEEK_SET); //go to the begining of the file
fclose(fp);
if ((_seqarray = (char *) malloc(bytes_expected/2)) == NULL) //allocate space for file
err(EX_OSERR, "data malloc");
string name;
string seqtemp;
string garbage;
string phredtemp;
boost::iostreams::stream<boost::iostreams::file_source>file(filename);
while (std::getline(file, name)) {
std::getline(file, seqtemp);
std::getline(file, garbage);
std::getline(file, phredtemp);
if (seqtemp.size() != SRlength) {
if (seqtemp.size() != 0)
printf("Error on read in fastq: size is invalidn");
} else {
_names.push_back(name);
strncpy( &(_seqarray[SRlength*_filelength]), seqtemp.c_str(), seqtemp.length()); //do not handle special letters here, do on GPU
_filelength++;
}
}
}
首先,您可以使用文件映射,而不是将文件读入内存。您只需要将程序构建为 64 位以容纳 3GB 的虚拟地址空间(对于 32 位应用程序,在用户模式下只能访问 2GB(。或者,您可以按部分映射和处理文件。
接下来,在我看来,您的瓶颈是"将行复制到向量"。处理向量涉及动态内存分配(堆操作(,这在关键循环中会非常严重地影响性能(。如果是这种情况 - 要么避免使用向量,要么确保它们在循环外部声明。后者会有所帮助,因为当您重新分配/清除矢量时,它们不会释放内存。
发布您的代码(或其中的一部分(以获取更多建议。
编辑:
似乎您所有的瓶颈都与字符串管理有关。
- 读入
std::string
std::getline(in, seqtemp);
处理动态内存分配。 -
_names.push_back(name);
这更糟。首先,按值将std::string
放入vector
中。表示 - 字符串被复制,因此发生另一个动态分配/释放。此外,当最终在内部重新分配vector
时 - 所有包含的字符串都会再次复制,并带来所有后果。
我建议既不使用标准格式的文件 I/O 函数 (Stdio/STL(,也不使用 std::string
。为了获得更好的性能,您应该使用指向字符串(而不是复制的字符串(的指针,如果映射整个文件,这是可能的。另外,您必须实现文件解析(划分为行(。
就像在这段代码中:
class MemoryMappedFileParser
{
const char* m_sz;
size_t m_Len;
public:
struct String {
const char* m_sz;
size_t m_Len;
};
bool getline(String& out)
{
out.m_sz = m_sz;
const char* sz = (char*) memchr(m_sz, 'n', m_Len);
if (sz)
{
size_t len = sz - m_sz;
m_sz = sz + 1;
m_Len -= (len + 1);
out.m_Len = len;
// for Windows-format text files remove the 'r' as well
if (len && 'r' == out.m_sz[len-1])
out.m_Len--;
} else
{
out.m_Len = m_Len;
if (!m_Len)
return false;
m_Len = 0;
}
return true;
}
};
如果_seqs
和_names
std::vectors
,并且您可以在处理整个 3GB 数据之前猜测它们的最终大小,您可以使用 reserve
来避免在回推循环中的新元素期间重新分配大部分内存。
您应该知道这样一个事实,即向量有效地在主内存中生成文件部分的另一个副本。因此,除非您有一个足够大的主内存来存储文本文件以及向量及其内容,否则您最终可能会遇到许多页面错误,这些错误也会对程序的速度产生重大影响。
自从使用getline
以来,您显然正在使用<stdio.h>
。
也许fopen
用fopen(path, "rm");
文件处理可能会有所帮助,因为m
告诉(它是一个GNU扩展(使用mmap
进行读取。
也许用setbuffer
设置一个大的缓冲区(即半兆字节(也会有所帮助。
也许,使用预读系统调用(也许在单独的线程中(可能会有所帮助。
但这一切都是猜测。你真的应该衡量事情。
一般建议:
- 以最简单、最直接、最干净的方法编写代码,
- 量
- 量
- 量
然后,如果所有其他方法都失败了:
- 读取页面对齐块中的原始字节(
read(2)
(。按顺序执行此操作,以便内核的预读对您有利。 - 重复使用同一缓冲区以最大程度地减少缓存刷新。
- 避免复制数据,就地解析,传递指针(和大小(。
mmap(2)
-ing [部分] 文件是另一种方法。这也避免了内核用户空间复制。
根据您的磁盘速度,使用非常快速的解压缩算法可能会有所帮助,例如fastlz(至少还有另外两个可能更有效,但在GPL下,因此许可证可能是一个问题(。
此外,使用C++数据结构和函数 car 可以提高速度,因为您可以实现更好的编译器时间优化。走C路并不总是斋戒!在某些糟糕的条件下,使用 char* 您需要解析整个字符串以达到 \0 产生灾难性的性能。
为了解析数据,使用 boost::spirit::qi 也可能是最优化的方法 http://alexott.blogspot.com/2010/01/boostspirit2-vs-atoi.html
- 警告处理为错误这里有什么问题
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 处理多个异常集合的C++方法
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 使用流处理接收到的数据
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 基于多个条件处理地图中的所有元素
- 如何用数字处理log(0)
- SSL上的`curl_easy_send`和`curl_asy_recv`:如何处理`CURLE_AGAIN`
- 错误处理.将系统错误代码映射到泛型
- 从文本文件中读取时钟时间和事件时间并进行处理
- 在运行时处理类型擦除的数据-如何不重新发明轮子
- 在for循环中使用auto vs decltype(vec.size())来处理字符串的向量
- 用于矢量处理的多个线程
- 对字符串进行排序时,在c++中处理sort()
- 如何处理linux终端中带有负号(-)的C++中的命令行参数
- 处理除以零会导致<csignal>意外行为
- 是否可以在c++中处理字符串流中的各个元素
- 在多个核心中处理一个HTTP请求