从文件中读取大量数据并以有效的方式解析日期.如何提高海量数据的性能
to read huge data from file and parse the date in efficient way. How to improve the performance for huge data?
我正在从文件中读取大量数据:
//ABC.txt
10 12 14 15 129
-12 14 -18 -900 -1234
145 12 13
12
32 68 51 76 -59 -025
- - - - etc
fun(char *p, int x, int y, int z) {
}
我尝试使用atoi
,strtok
,但是当数组太大并且sscanf
也非常慢时,它们会实时消耗。
如何提高海量数据的性能?
我正在使用strtok
进行解析。我正在寻找快速方法来解析每一行。
我正在阅读每一行,然后将每一行解析为:
char * ptr;
ptr = strtok (str," ");
while (ptr != NULL)
{
int value1 = atoi(ptr) ;
ptr = strtok (NULL, " ");
}
- 有没有快速的方法将字符串解析为
int
? - 有没有比代码更快的替代方法?我正在使用
atoi
将char *
转换为int
。 - 我可以使用其他快速方法将
char *
转换为int
吗?
你找错地方了。 问题不是解析,除非你正在做一些真正奇怪的事情。 在现代N Ghz CPU上,每行所需的周期很小。 影响性能的是物理 I/O。 旋转的东西往往以 10 秒/秒的速度运行。
我也怀疑问题是文件的物理读取,因为这将有效地缓存在文件系统缓存中。
不,正如 samy.vilar 所暗示的那样,问题几乎可以肯定是虚拟内存问题:
。阵列太大了...
使用系统监视器/psinfo/top 查看您的应用程序。 几乎可以肯定的是,当它构建内存中阵列并且您的操作系统将其分页到磁盘时,它正在增长一个大型工作集。
所以忘记阅读是一个问题。 你真正的问题是如何操作内存中的大数据集。 这里的方法多种多样:
- 不要。 批量处理数据并操作批处理。
- 使用节省空间的存储(例如紧凑型元件)。
- 分配更多内存资源。
SO有很多关于这个问题的讨论。
要将 ASCII 字符串转换为整数值,您不能比 atoi
正在执行的操作快得多,但您可以通过实现内联使用的转换函数来加快速度。下面的版本将指针递增到扫描的数字以上,因此它与atoi
语义不匹配,但它应该有助于提高解析器效率,如下图所示。(显然缺少错误检查,因此如果需要,请添加它。
static inline int my_parsing_atoi(const char *&s) {
if (s) {
bool neg = false;
int val = 0;
if (*s == '-') { neg = true; ++s; }
for (;isdigit(*s);++s) val = 10*val + (*s - '0');
return neg ? -val : val;
}
return 0;
}
const char *p = input_line;
if (p) {
p += strspn(p, " ");
while (*p) {
int value1 = my_parsing_atoi(p);
p += strspn(p, " ");
}
}
确保已正确分析代码,以便知道例程受计算限制,而不是 I/O 限制。大多数情况下,您将受到 I/O 限制,以下建议是缓解它的方法。
如果您使用的是 C 或 C++ 文件读取例程,例如 fread
或 fstream
,您应该获得缓冲读取,这应该已经非常有效,但您可以尝试使用底层操作系统调用,例如 POSIX read
,一次读取较大块中的文件以加快文件读取效率。为了真正花哨,您可以在处理文件时对文件执行异步读取,方法是使用线程或使用 aio_read
。您甚至可以使用 mmap
,这将消除一些数据复制开销,但如果文件非常大,则需要管理地图,以便munmap
已扫描的文件部分并mmap
要扫描的新部分中。
我使用如下所示的代码对上面的解析例程和 OP 的例程进行了基准测试:
clock_t before_real;
clock_t after_real;
struct tms before;
struct tms after;
std::vector<char *> numbers;
make_numbers(numbers);
before_real = times(&before);
for (int i = 0; i < numbers.size(); ++i) {
parse(numbers[i]);
}
after_real = times(&after);
std::cout << "user: " << after.tms_utime - before.tms_utime
<< std::endl;
std::cout << "real: " << after_real - before_real
<< std::endl;
real
和 user
之间的区别在于,real
是挂钟时间,而user
是运行进程的操作系统花费的实际时间(因此上下文切换不计入运行时间)。
测试使我的例程运行速度几乎是OP例程(在64位Linux系统上用g++ -O3
编译)的两倍。
如果你的文件真的很大,那么 IO 就是杀死你的,而不是解析。每次读取一行时,您都在执行系统调用,这可能非常昂贵。
更有效的替代方法可能是使用内存映射文件 IO。如果您使用的是 POSIX 系统(如 Linux),则可以使用 mmap
命令,该命令一次加载所有文件并返回指向其在内存中的位置的指针。然后,内存管理器负责在您通过该指针访问数据时读取和交换文件。
这看起来像这样
#include <sys/mman.h>
int fd = open( 'abc.txt' , O_RDONLY );
char *ptr = mmap( NULL , length , PROT_READ , MAP_PRIVATE , fd , 0 );
但我强烈建议您阅读手册页并为自己找到最佳选择。
-
如果您的文件包含 int 数字,则可以使用 operator>>,但这是 c++ 唯一的解决方案。像这样:
std::fstream f("abc.txt"); int value = 0; f >> value
-
如果将文件转换为包含二进制数表示形式,您将有更多选项来提高性能。它不仅可以避免将数字从字符串解析为类型,而且您还有其他选项来访问数据(例如使用 mmap)。
首先,一般建议始终使用性能分析来检查是否确实是翻译缓慢,而不是其他原因,例如从磁盘物理读取文件。
您可以通过编写自己的最小数字解析函数来提高性能。 strtok 修改字符串,因此它不会以最佳速度,如果您知道所有数字都是十进制整数,并且不需要任何错误检查,则可以稍微简化翻译。
一些没有strtok的代码可能会加快一行的处理速度,如果它实际上是翻译而不是(例如)I/O是问题所在。
void handle_one_number(int number) {
// ....
}
void handle_numbers_in_buffer(char *buffer) {
while (1) {
while (*buffer != ' ' && isspace(*buffer))
++buffer;
if (*buffer == ' ')
return;
int negative = 0;
if (*buffer == '-') {
negative = 1;
++buffer;
}
int number = 0;
while (isdigit(*buffer)) {
number = number * 10 + *buffer - '0';
++buffer;
}
if (negative)
number = -number;
handle_one_number(number);
}
}
我实际上去运行了一些基准测试。我原本预计 I/O 会占主导地位,但事实证明(通常关于"在我的特定系统上,使用我的特定编译器")数字解析需要相当多的时间。
通过从 strtok 版本更改为上面的代码,我设法将 1 亿个数字(文本已经在内存中)的翻译时间从 5.2 秒缩短到大约 1.1 秒。当从慢速磁盘(鱼子酱绿)读取时,我测量到从5.9秒到3.5秒的改进。从 SSD 读取时,我测量到从 5.8 秒到 1.8 秒的改进。
我还尝试使用 while (fscanf(f, "%d", ....) == 1) ....
直接读取文件,但结果要慢得多(10 秒),可能是因为 fscanf 是线程安全的,更多的调用需要更多的锁定。
(Ubuntu 11.04 上的 GCC 4.5.2 具有 -O2 优化,每个版本多次执行,在运行之间刷新磁盘缓存,i7 处理器。
- 在c代码之间共享数据的最佳方式
- 使用QQuickFramebufferObject时同步数据的最佳方式是什么
- 我可以使用哪种数据结构来处理这种方式
- 打印从SQLite3数据库检索的数据C++与命令行中相同的方式
- 如何以"union-like"方式更改C++向量的数据类型
- DLL共享数据的推荐方式是什么
- C++中数据初始化的不同方式
- 在编译时解压缩数组扩展数据块 (C++11/14)
- 使用 QT C++过滤大数据的最佳方式
- 优化使用 C++ 查询 SQLite DB 中超过 5000 万条数据记录的方式
- 数组数据以错误的方式遍历 Python/Matlab
- 这种抽象模板矩阵类数据类型的方式好吗?
- 当算法需要派生类的知识时,将算法与数据解耦
- 解压缩 TIFF 数据会打印随机字符
- 解压缩压缩的 TIFF 数据
- 返回二进制数据的通用方式,而无需原始指针
- 二进制文件中的某些数据如图所示,并以奇怪的方式显示OS
- 以面向对象的方式解析自定义数据包
- GCC 以错误的方式解包(我<...)折叠表达式
- 我可以将数据分配给从OpenCV对象解引用中检索的双函数返回以任何方式