【问题标题】:performance comparison - pcap file reading: C++'s ifstream VS C's fread性能比较——pcap 文件读取:C++ 的 ifstream VS C 的 fread
【发布时间】:2016-11-18 10:28:04
【问题描述】:

我正在研究哪个是更快的二进制文件阅读器:C++ 的 ifstream::read 或 C 的 fread。

根据网上的说法,包括类似的问题,并没有太大的区别,所以我决定去挖dipper。

我使用了一个 1.22gb 的 pcap 文件,其中包含大约 1,377,000 个数据包。 两个程序都使用 mingw32-g++ 编译,没有优化。

头结构是根据wireshark的wiki-libpcap文件结构定义的: https://wiki.wireshark.org/Development/LibpcapFileFormat

这是 C 代码:

#include <stdio.h>
#include <stdlib.h>
#include <Winsock2.h>

/* definition of structs: pcap_global_header, pcap_packet_header, ethernet_header, ipv4_header, tcp_header */

int main()
{
    int count = 0, bytes_read;

    /* open file */
    FILE * file = fopen("test.pcap", "rb");

    /* read file header */
    struct pcap_global_header gheader;

    fread(&gheader, sizeof(char), sizeof(struct pcap_global_header), file);

    // if not ethernet type
    if(gheader.network != 1)
    {
        printf("not ethernet !\n");
        return 1;
    }

    /* read packets */
    char *buffer = (char*)malloc(gheader.snaplen);

    struct pcap_packet_header pheader;
    struct ether_header eth;
    struct ipv4_header ip;
    struct tcp_header tcp;

    fread(&pheader, sizeof(char), sizeof(struct pcap_packet_header), file);

    while(!feof(file))
    {
        ++count;

        bytes_read = fread(&eth, sizeof(char), sizeof(struct ether_header), file);

        // ip
        if(eth.type == 0x08)
        {
            bytes_read += fread(&ip, sizeof(char), sizeof(struct ipv4_header), file);

            //tcp
            if( ip.protocol == 0x06 )
            {
                bytes_read += fread(&tcp, sizeof(char), sizeof(struct tcp_header), file);
            }
        }

        //read rest of the packet
        fread(buffer, sizeof(char), pheader.incl_len - bytes_read, file);

        // read next packet's header
        fread(&pheader, sizeof(char), sizeof(struct pcap_packet_header), file);
    }

    printf("(C) total packets: %d\n", count);

    return 0;
}

这是 C++ 代码:

#include <iostream>
#include <fstream>
#include <memory>

#include <Winsock2.h>

/* definition of structs: pcap_global_header, pcap_packet_header, ethernet_header, ipv4_header, tcp_header */

int main()
{
    int count_packets = 0, bytes_read;

    /* open file */
    std::ifstream file("test.pcap", std::fstream::binary | std::fstream::in);

    /* read file header */
    struct pcap_global_header gheader;

    file.read((char*)&gheader, sizeof(struct pcap_global_header));

    // if not ethernet type
    if(gheader.network != 1)
    {
        printf("not ethernet !\n");
        return 1;
    }

    /* read packets */
    char *buffer = std::allocator<char>().allocate(gheader.snaplen);

    struct pcap_packet_header pheader;
    struct ether_header eth;
    struct ipv4_header ip;
    struct tcp_header tcp;

    file.read((char*)&pheader, sizeof(pcap_packet_header));

    while(!file.eof())
    {
        ++count_packets;

        file.read((char*)&eth, sizeof(struct ether_header));
        bytes_read = sizeof(struct ether_header);

        // ip
        if(eth.type == 0x08)
        {
            file.read((char*)&ip, sizeof(struct ipv4_header));
            bytes_read += sizeof(struct ipv4_header);

            //tcp
            if( ip.protocol == 0x06 )
            {
                file.read((char*)&tcp, sizeof(struct tcp_header));
                bytes_read += sizeof(struct tcp_header);
            }
        }

        // read rest of the packet
        file.read(buffer, pheader.incl_len - bytes_read);

        // read next packet's header
        file.read((char*)&pheader, sizeof(pcap_packet_header));
    }

    std::cout << "(C++) total packets :" << count_packets << std::endl;

    return 0;
}

结果非常令人失望:

C代码结果:

(C) total packets: 1377065

Process returned 0 (0x0)   execution time : 1.031 s
Press any key to continue.

C++代码结果:

(C++) total packets :1377065

Process returned 0 (0x0)   execution time : 3.172 s
Press any key to continue.

显然,我对每个版本都运行了几次,因此,我正在寻找一种使用 C++ 读取文件的更快方法。

【问题讨论】:

  • 我正在寻找一种使用 C++ 读取文件的更快方法。您找到了 - 使用 ::fread()。另见Why is “while ( !feof (file) )” always wrong?
  • 没有优化?为什么要在没有优化的情况下进行基准测试?
  • @AndrewHenle 我用错了 feof() 吗?
  • @J.Doe 我用错了 feof() 吗? 是的。阅读链接的问题和答案。 feof() 直到尝试读取文件末尾之后才为真。
  • @AndrewHenle 我在调用 feof() 之前读取了第一个数据包的标头,这里没有任何问题

标签: c++ c performance pcap


【解决方案1】:

ifstream::read() 将数据从内部缓冲区复制到您的缓冲区。它导致性能的主要差异。您可以尝试通过pubsetbuf 来克服它并用您自己的缓冲区替换内部缓冲区:

std::ifstream file;
char buf[1024];
file.rdbuf()->pubsetbuf(buf, sizeof buf);

问题是这个函数是实现定义的,在大多数情况下你仍然需要使用额外的数据副本。

在您的情况下,您不需要ifstream 的所有功能,因此为了性能和简单性,我建议使用&lt;cstdio&gt;

【讨论】:

  • “ifstream 的所有功能”是什么意思?速度不达标有什么好处?
  • @J.Doe 不是每个人都需要尽可能好的性能。 ifstream 实现了高级std::basic_istream,因此您可以将其用作输入流。有许多有用的标准算法可用于ifstream,例如std::transform、迭代器等。在许多情况下,所有这些都让生活变得更简单,但并非总是如此。
【解决方案2】:

fread() 应该总是更快,因为它将字节直接读取到您的缓冲区中,无需额外处理(此处不需要)。

另外,最好一次读取整个数据包,而不是为每个数据包调用 4 次 fread()。例如,您可以在缓冲区上使用ether_header*

使用mmap() 而不是fread() 应该会给您额外的加速(无需将数据从内核模式复制到用户模式缓冲区)。对于 Windows,请参阅 CreateFileMapping()MapViewOfFile() - 这允许您使用指针直接访问文件内容,就像它是一个大内存缓冲区一样。

【讨论】:

    猜你喜欢
    • 2016-09-05
    • 2013-10-11
    • 2014-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-18
    相关资源
    最近更新 更多