1

问题:在 Visual C++ 中将 10 GB BYTE 数组转换为十六进制格式的标准字符串的最快方法是什么?

我在做什么:我正在使用 std::fread(...) 将一个非常大的文件读入一个大缓冲区,然后将其格式化为十六进制格式,然后将其转换为 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

这里的问题不太可能出在将数据转换为十六进制的例程中。

问题几乎可以肯定是您只是使用太多的内存。输入的每个字节变成两个十六进制字节。如果在它们之间添加空格,则每个输入都会产生三个字节的输出。

如果您从 10 GB 的输入开始,这意味着您将产生 20 或 30 GB 的输出。由于您正在逐步扩展目标字符串,因此它很有可能会调整其缓冲区的大小并在达到完整的 30 GB 之前多次复制数据。在调整大小/复制操作期间,它同时需要旧副本新副本的内存空间。根据它在调整大小时使用的因素,您正在使用(或尝试使用)大约 60 GB RAM 的更改是好的。除非您实际上拥有至少 64 GB 的物理 RAM,否则这几乎肯定会非常慢。

很有可能您最好通过从一个文件读取并写入另一个文件来进行处理。公平地说,除非您拥有非常快的硬盘驱动器,否则这仍然不会非常快- 并且您非常喜欢从一个读取并写入另一个。

除非您确实拥有 64Gig 的物理 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 为真,将第二个参数更改为ostream_iteratorfrom ""to " "

对于这么大的文件,您可能希望自己处理文件 - 因为您显然是在 Windows 上运行,对于这种大小的文件,您可能可以通过CreateFile直接使用并指定FILE_FLAG_NO_BUFFERING避免颠簸来获得相当多的收益在执行此操作时缓存。读取 4 兆字节左右的块,转换为另一个块,然后写出结果。如果您有两张(或更多)光盘,以便在写入另一张时可以从其中读取,您还可以考虑使用重叠 I/O 来允许从一个文件读取、写入另一个文件以及同时进行处理。如果您只使用一张光盘,那仍然允许处理和 I/O 并行发生,但处理速度将比 I/O 快得多,因此它可能无法获得足够的努力来证明努力的合理性。

于 2012-11-17T04:03:01.727 回答
0

我保证这将在最快实现的 epsilon 范围内:

#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 使用自己的内部缓冲,因此您不会一次向操作系统询问一个字符(操作系统还在后台进行自己的磁盘缓冲)。

如果您决定使用 C++,请更改#include <stdio.h>#include <cstdio>并在之后添加一个using namespace std;。C 运行时库是标准 C++ 库的一部分,经验表明它往往比使用 iostreams 快得多,可能是因为它不受语言环境的影响。

于 2012-11-17T05:23:40.507 回答