在我的程序中我想读取几个文本文件(超过800个文件),每个文件有256行,文件名从1.txt到n.txt,并在几个处理步骤后存储到数据库中。我的问题是数据的读取速度。通过使用OpenMP多线程读取循环,我可以将程序速度提高到以前的两倍。有没有办法加快速度?我的实际代码是
std::string CCD_Folder = CCDFolder; //CCDFolder is a pointer to a char array
int b = 0;
int PosCounter = 0;
int WAVENUMBER, WAVELUT;
std::vector<std::string> tempstr;
std::string inputline;
//Input
omp_set_num_threads(YValue);
#pragma omp parallel for private(WAVENUMBER) private(WAVELUT) private(PosCounter) private(tempstr) private(inputline)
for(int i = 1; i < (CCD_Filenumbers+1); i++)
{
//std::cout << omp_get_thread_num() << ' ' << i << '\n';
//Umwandlung und Erstellung des Dateinamens, Öffnen des Lesekanals
std::string CCD_Filenumber = boost::lexical_cast<string>(i);
std::string CCD_Filename = CCD_Folder + '\\' + CCD_Filenumber + ".txt";
std::ifstream datain(CCD_Filename, std::ifstream::in);
while(!datain.eof())
{
std::getline(datain, inputline);
//Processing
};
};
此处未定义的所有变量都在我的代码中的其他位置定义,并且正在运行。那么有可能加快这个代码的速度吗?
非常感谢你!
答案 0 :(得分:8)
一些实验:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
void generateFiles(int n) {
char fileName[32];
char fileStr[1032];
for (int i=0;i<n;i++) {
sprintf( fileName, "c:\\t\\%i.txt", i );
FILE * f = fopen( fileName, "w" );
for (int j=0;j<256;j++) {
int lineLen = rand() % 1024;
memset(fileStr, 'X', lineLen );
fileStr[lineLen] = 0x0D;
fileStr[lineLen+1] = 0x0A;
fileStr[lineLen+2] = 0x00;
fwrite( fileStr, 1, lineLen+2, f );
}
fclose(f);
}
}
void readFiles(int n) {
char fileName[32];
for (int i=0;i<n;i++) {
sprintf( fileName, "c:\\t\\%i.txt", i );
FILE * f = fopen( fileName, "r" );
fseek(f, 0L, SEEK_END);
int size = ftell(f);
fseek(f, 0L, SEEK_SET);
char * data = (char*)malloc(size);
fread(data, size, 1, f);
free(data);
fclose(f);
}
}
DWORD WINAPI readInThread( LPVOID lpParam )
{
int * number = (int *)lpParam;
char fileName[32];
sprintf( fileName, "c:\\t\\%i.txt", *number );
FILE * f = fopen( fileName, "r" );
fseek(f, 0L, SEEK_END);
int size = ftell(f);
fseek(f, 0L, SEEK_SET);
char * data = (char*)malloc(size);
fread(data, size, 1, f);
free(data);
fclose(f);
return 0;
}
int main(int argc, char ** argv) {
long t1 = GetTickCount();
generateFiles(256);
printf("Write: %li ms\n", GetTickCount() - t1 );
t1 = GetTickCount();
readFiles(256);
printf("Read: %li ms\n", GetTickCount() - t1 );
t1 = GetTickCount();
const int MAX_THREADS = 256;
int pDataArray[MAX_THREADS];
DWORD dwThreadIdArray[MAX_THREADS];
HANDLE hThreadArray[MAX_THREADS];
for( int i=0; i<MAX_THREADS; i++ )
{
pDataArray[i] = (int) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(int));
pDataArray[i] = i;
hThreadArray[i] = CreateThread(
NULL,
0,
readInThread,
&pDataArray[i],
0,
&dwThreadIdArray[i]);
}
WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);
printf("Read (threaded): %li ms\n", GetTickCount() - t1 );
}
第一个函数只是制作一个测试数据集的丑陋的东西(我知道它可以做得更好,但我老实说没有时间)
第一个实验 - 顺序读取 第二个实验 - 并行阅读
结果:
256个文件:
Write: 250 ms
Read: 140 ms
Read (threaded): 78 ms
1024个文件:
Write: 1250 ms
Read: 547 ms
Read (threaded): 843 ms
我认为第二次尝试清楚地表明,从长远来看,“愚蠢”的线程创建只会让事情变得更糟。当然,它需要在预分配工作者,某些线程池等方面进行改进,但我认为通过从磁盘读取100-200k这样快速的操作,将此功能转移到线程中并没有什么好处。我没有时间编写更“聪明”的解决方案,但我怀疑它会更快,因为你必须为互斥锁等添加系统调用......
走极端,你可以想到预先分配内存池等..但正如在代码之前提到你发布的错误..这是几毫秒的问题,但肯定不是秒
800个文件(每行20个字符,256行)
Write: 250 ms
Read: 63 ms
Read (threaded): 500 ms
结论:
答案是:
您的阅读代码错误,您正在阅读文件这么慢,速度显着提高,然后您可以并行运行任务。在上面的代码中,阅读实际上是更快,然后费用来生成线程
答案 1 :(得分:1)
我会尝试使用C代码来读取文件。我怀疑它会更快。
FILE* f = ::fopen( CCD_Filename.c_str(), "rb" );
if( f == NULL )
{
return;
}
::fseek( f, 0, SEEK_END );
const long lFileBytes = ::ftell( f );
::fseek( f, 0, SEEK_SET );
char* fileContents = new char[lFileBytes + 1];
const size_t numObjectsRead = ::fread( fileContents, lFileBytes, 1, f );
::fclose( f );
if( numObjectsRead < 1 )
{
delete [] fileContents;
return;
}
fileContents[lFileBytes] = '\0';
// assign char buffer of file contents here
delete [] fileContents;
答案 2 :(得分:1)
您的主要瓶颈是从硬盘上物理读取。
除非您将文件放在不同的驱动器上,否则驱动器一次只能读取一个文件中的数据。最好的办法是将每个文件作为一个整体读取,而不是读取一个文件的一部分,告诉驱动器找到另一个文件,从那里读取,然后重复。将驱动器头重新定位到其他位置,尤其是其他文件,通常比让驱动器完成读取单个文件更昂贵。
下一个瓶颈是处理器和硬盘之间的数据通道。如果您的硬盘驱动器共享任何类型的通信通道,您将看到瓶颈,因为每个驱动器的数据必须通过通信通道连接到您的处理器。您的处理器将通过此通信通道(PATA,SATA,USB等)向驱动器发送命令。
接下来的步骤的目的是减少程序内存和硬盘通信接口之间“中间人”的开销。最有效的是直接访问控制器;效率较低的是使用OS功能; “C”函数(fread
和familiy),最少的是C ++流。随着效率的提高,与平台的紧密耦合降低了安全性(简单性)。
我建议如下:
如果您可以访问DMA通道,请使用它们从硬盘驱动器读取“读取缓冲区”。
接下来,您可以优化代码以有效地使用处理器的数据缓存。例如,设置“处理”,使数据结构不超过缓存中的数据行。此外,优化代码以使用寄存器(指定register
关键字或使用语句块,以便编译器知道何时可以重用变量)。
可能有所帮助的其他优化:
答案 3 :(得分:0)
您可能达到了磁盘的读取限制,这意味着您的选项有限。如果这是一个常见问题,您可以考虑使用不同的RAID结构,这将为您提供更高的读取吞吐量,因为多个读取头可以同时访问数据。
要查看磁盘访问是否确实是瓶颈,请使用time命令运行程序:
>> /usr/bin/time -v <my program>
在输出中,您将看到您使用的CPU时间与磁盘访问所需的时间相比。