从文件中读取大量数据并以有效的方式解析日期.如何提高海量数据的性能

to read huge data from file and parse the date in efficient way. How to improve the performance for huge data?

本文关键字:数据 方式解 日期 性能 何提高 文件 读取 有效      更新时间:2023-10-16

我正在从文件中读取大量数据:

/

/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) {
}

我尝试使用atoistrtok,但是当数组太大并且sscanf也非常慢时,它们会实时消耗。

如何提高海量数据的性能?

我正在使用strtok进行解析。我正在寻找快速方法来解析每一行。

我正在阅读每一行,然后将每一行解析为:

 char * ptr;
 ptr = strtok (str," ");
 while (ptr != NULL)
 {
    int value1 = atoi(ptr) ;
    ptr = strtok (NULL, " ");
 }
  • 有没有快速的方法将字符串解析为int
  • 有没有比代码更快的替代方法?我正在使用atoichar *转换为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++ 文件读取例程,例如 freadfstream ,您应该获得缓冲读取,这应该已经非常有效,但您可以尝试使用底层操作系统调用,例如 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;

realuser 之间的区别在于,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 );

但我强烈建议您阅读手册页并为自己找到最佳选择。

  1. 如果您的文件包含 int 数字,则可以使用 operator>>,但这是 c++ 唯一的解决方案。像这样:

    std::fstream f("abc.txt");
    int value = 0;
    f >> value
    
  2. 如果将文件转换为包含二进制数表示形式,您将有更多选项来提高性能。它不仅可以避免将数字从字符串解析为类型,而且您还有其他选项来访问数据(例如使用 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 处理器。