2016-11-18 23 views
0

私は、より高速のバイナリファイルリーダーを研究していました。C++のifstream :: readまたはCのフリーダです。パフォーマンスの比較 - pcapファイルの読み込み:C++のifstream VS Cのフリーダイヤル

インターネットによれば、類似の質問を含むは、それほど違いはないので、私はディッパを掘ることにしました。

私は1.22ギガバイトのpcapファイルを使用しました。このファイルには、約1,377,000パケットが含まれています。 どちらのプログラムもmingw32-g ++を使用してコンパイルされています。

ヘッダ構造体は、Wiresharkののウィキに従って定義される - の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++を使用してファイルを読み込むためのより高速な方法を探しています。

+0

*私はC++を使ってファイルを読むより速い方法を探しています*あなたはそれを見つけました - ':: fread()'を使ってください。また、[while(!feof(file))が常に間違っているのはなぜですか?](http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) –

+2

最適化されていません?最適化をしないと、これをベンチマークするのはなぜですか? – Banex

+0

@AndrewHenle私はfeof()を間違って使用していますか? –

答えて

2

ifstream::read()は、内部バッファからバッファにデータをコピーします。パフォーマンスの主な違いが発生します。あなたはそれを克服し、pubsetbufを経由して、独自に内部バッファを置き換えるために試みることができる:

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

問題は、この機能は実装定義されており、ほとんどの場合、あなたはまだ余分なデータのコピーを使用する必要があるということです。

あなたのケースでは、ifstreamのすべてのパワーを必要としないので、パフォーマンスと簡潔さのために、<cstdio>を使用することをお勧めします。

+1

「ifstreamのすべての力」はどういう意味ですか?スピードが落ちれば何が良いのでしょうか? –

+1

@ J.Doe誰もが最高のパフォーマンスを必要とするわけではありません。 'ifstream'は高水準の' std :: basic_istream'を実装しているので、入力ストリームとして使うことができます。 'ifstream'のために使用できる多くの有用なstdアルゴリズムがあります。 'std :: transform'、イテレータなどがあります。多くの場合、すべてが簡単になりますが、必ずしもそうとは限りません。 – Nikita

1

fread()は、余分な処理(ここでは不要)を行わずにバッファに直接バイトを読み込むため、常に高速にする必要があります。

また、パケットごとにfread()を4回呼び出すのではなく、パケット全体を一度に読み取る方がよい場合があります。たとえば、バッファーにether_header*を使用できます。

fread()の代わりにmmap()を使用すると、高速化(カーネルモードからユーザーモードバッファーにデータをコピーする必要はありません)が可能になります。 Windowsの場合CreateFileMapping()MapViewOfFile()を参照してください。これにより、ファイルの内容に大きなメモリバッファのようにポインタで直接アクセスできます。

関連する問題