1

質問: Visual C ++で10GBのBYTE配列を16進形式の標準文字列に変換する最速の方法は何ですか?

私がしていること:私はstd :: fread(...)を使用して非常に大きなファイルを大きなバッファーに読み込み、それを16進形式にフォーマットしてから、std::stringに変換しています。私は理にかなっていると思います。

私は現在、遅いこのコード(私が書いたものではありません...)を使用しています。

std::string ByteToHexFormatStdStr( __in ::BYTE *ByteArray, __in int ArraySize, __in bool AddSpace )
{
    ::BYTE Byte = NULL;
    const char HexCharacters[ 16 ] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    std::string Return = "";

    for( ::UINT Index = 0; Index < ArraySize; ++ Index )
    {
        Byte = ( ::BYTE )( ByteArray[ Index ] & 0xF0 );
        Byte = ( ::BYTE )( Byte >> 4 );
        Byte = ( ::BYTE )( Byte & 0x0F );
        Return += HexCharacters[ ( int )Byte ];
        Byte = ( ::BYTE )( ByteArray[ Index ] & 0x0F );
        Return += HexCharacters[ ( int )Byte ];

        if( AddSpace ) Return += ' ';
    }

    return ( Return );
}
4

2 に答える 2

3

ここでの問題は、データを16進数に変換するルーチンにある可能性は低いです。

問題は、ほぼ確実に、メモリを使いすぎていることです。入力の各バイトは16進数の2バイトになります。それらの間にスペースを追加すると、入力ごとに3バイトの出力が作成されます。

10ギガバイトの入力から始めている場合、それは20ギガバイトまたは30ギガバイトの出力を生成していることを意味します。宛先文字列を段階的に拡張しているため、バッファのサイズを変更し、データが30ギガバイトに達する前にデータを数回コピーする可能性があります。サイズ変更/コピー操作中に、古いコピーと新しいコピーを同時に使用するためのメモリスペースが必要です。サイズ変更時に使用する要素にもよりますが、約60ギガバイトのRAMを使用している(または使用しようとしている)変更は適切です。実際に少なくとも64ギガバイトの物理RAMがない限り、それはほぼ確実にかなり遅くなります。

あるファイルから読み取って別のファイルに書き込むことで処理を行う方がよい可能性はかなりあります。公平を期すために、ハードドライブが本当に高速でない限り、これはまだ極端に高速になることはありません。強い好みで、あるドライブから読み取り、別のドライブに書き込みます。

64ギガの物理RAMがない限り、ファイルからファイルへの処理は、仮想メモリを使用するよりもほぼ確実に高速です。

std::string ToHex(char input)
{
    const char Hex[] = "0123456789ABCDEF";
    std::string Return;

    Return += Hex[(unsigned)input>>4 & 0xf];
    Return += Hex[(unsigned)input & 0xf];
    return Return;
}

std::transform(std::istream_iterator<char>(infile),
               std::istream_iterator<char>(),
               std::ostream_iterator<std::string>(outfile, ""),
               ToHex);

AddSpaceがtrueであるのと同等の場合、2番目のパラメーターをostream_iteratorfromから""に変更します" "

CreateFileこの大きなファイルの場合は、独自のファイル処理を行うことをお勧めします。Windowsで実行しているように見えるため、このサイズのファイルの場合は、直接使用し、FILE_FLAG_NO_BUFFERINGスラッシングを回避するように指定することで、かなりの利益を得ることができます。これを行うときにキャッシュします。たとえば、4メガバイト程度のチャンクで読み取り、別のチャンクに変換して、結果を書き出します。2つ(またはそれ以上)のディスクがあり、一方から他方に書き込むときに読み取ることができる場合は、オーバーラップしたI / Oを使用して、一方のファイルからの読み取り、もう一方のファイルへの書き込み、および処理を同時に実行できるようにすることも検討できます。1枚のディスクのみを使用している場合でも、処理とI / Oを並行して実行できますが、処理はI / Oよりも十分に高速であるため、作業を正当化するのに十分な効果は得られない可能性があります。

于 2012-11-17T04:03:01.727 に答える
0

私はこれが可能な限り最速の実装のイプシロン内にあることを保証します:

#define _CRT_DISABLE_PERFCRIT_LOCKS
#include <stdio.h>
#include <io.h>
#include <fcntl.h>

int main(int argc, char **argv) {
    _setmode(fileno(stdin), _O_BINARY);
    _setmode(fileno(stdout), _O_BINARY);

    char hex[] = "0123456789ABCDEF";
    int c;
    while ((c = getchar()) != EOF) {
        putchar(hex[c >> 4]);
        putchar(hex[c & 0xF]);
    }

    return 0;
}

コンパイルして実行しthisprog < in > outます。

MSVC ++では、stdio操作はロックを使用して、マルチスレッドコードでスレッドセーフな動作を可能にします。このシングルスレッドプログラムではそれは必要ないので、トップライン(ここで説明)でオフにします。これにより、処理速度が大幅に向上します。_setmode()標準の入力ストリームと出力ストリームのバイナリモードをオンにするための呼び出し。これらはデフォルトでテキストモード(入力に\r\n変換さ\nれ、その逆も同様)になっています。

stdioは独自の内部バッファリングを使用するため、高速です。したがって、一度に1文字ずつOSに要求することはありません(OSはバックグラウンドで独自のディスクバッファリングを追加で実行します)。

C ++を使用することにした場合は、に変更#include <stdio.h>して後で#include <cstdio>追加してください。using namespace std;Cランタイムライブラリは標準C++ライブラリの一部であり、経験上、おそらくロケールに煩わされないため、iostreamを使用するよりもはるかに高速になる傾向があります。

于 2012-11-17T05:23:40.507 に答える